mirror of
https://github.com/wassname/phaser.git
synced 2026-08-04 13:14:14 +08:00
Tilemap collision working but needs speeding up
This commit is contained in:
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Nodejs libs.
|
||||
var path = require('path');
|
||||
|
||||
// This allows grunt to require() .coffee files.
|
||||
require('coffee-script');
|
||||
|
||||
// The module to be exported.
|
||||
var grunt = module.exports = {};
|
||||
|
||||
// Expose internal grunt libs.
|
||||
function gRequire(name) {
|
||||
return grunt[name] = require('./grunt/' + name);
|
||||
}
|
||||
var util = gRequire('util');
|
||||
gRequire('template');
|
||||
gRequire('event');
|
||||
var fail = gRequire('fail');
|
||||
gRequire('file');
|
||||
var option = gRequire('option');
|
||||
var config = gRequire('config');
|
||||
var task = gRequire('task');
|
||||
var log = gRequire('log');
|
||||
var help = gRequire('help');
|
||||
gRequire('cli');
|
||||
var verbose = grunt.verbose = log.verbose;
|
||||
|
||||
// Expose some grunt metadata.
|
||||
grunt.package = require('../package.json');
|
||||
grunt.version = grunt.package.version;
|
||||
|
||||
// Expose specific grunt lib methods on grunt.
|
||||
function gExpose(obj, methodName, newMethodName) {
|
||||
grunt[newMethodName || methodName] = obj[methodName].bind(obj);
|
||||
}
|
||||
gExpose(task, 'registerTask');
|
||||
gExpose(task, 'registerMultiTask');
|
||||
gExpose(task, 'registerInitTask');
|
||||
gExpose(task, 'renameTask');
|
||||
gExpose(task, 'loadTasks');
|
||||
gExpose(task, 'loadNpmTasks');
|
||||
gExpose(config, 'init', 'initConfig');
|
||||
gExpose(fail, 'warn');
|
||||
gExpose(fail, 'fatal');
|
||||
|
||||
// Expose the task interface. I've never called this manually, and have no idea
|
||||
// how it will work. But it might.
|
||||
grunt.tasks = function(tasks, options, done) {
|
||||
// Update options with passed-in options.
|
||||
option.init(options);
|
||||
|
||||
// Display the grunt version and quit if the user did --version.
|
||||
var _tasks, _options;
|
||||
if (option('version')) {
|
||||
// Not --verbose.
|
||||
log.writeln('grunt v' + grunt.version);
|
||||
|
||||
if (option('verbose')) {
|
||||
// --verbose
|
||||
verbose.writeln('Install path: ' + path.resolve(__dirname, '..'));
|
||||
// Yes, this is a total hack, but we don't want to log all that verbose
|
||||
// task initialization stuff here.
|
||||
grunt.log.muted = true;
|
||||
// Initialize task system so that available tasks can be listed.
|
||||
grunt.task.init([], {help: true});
|
||||
// Re-enable logging.
|
||||
grunt.log.muted = false;
|
||||
|
||||
// Display available tasks (for shell completion, etc).
|
||||
_tasks = Object.keys(grunt.task._tasks).sort();
|
||||
verbose.writeln('Available tasks: ' + _tasks.join(' '));
|
||||
|
||||
// Display available options (for shell completion, etc).
|
||||
_options = [];
|
||||
Object.keys(grunt.cli.optlist).forEach(function(long) {
|
||||
var o = grunt.cli.optlist[long];
|
||||
_options.push('--' + (o.negate ? 'no-' : '') + long);
|
||||
if (o.short) { _options.push('-' + o.short); }
|
||||
});
|
||||
verbose.writeln('Available options: ' + _options.join(' '));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Init colors.
|
||||
log.initColors();
|
||||
|
||||
// Display help and quit if the user did --help.
|
||||
if (option('help')) {
|
||||
help.display();
|
||||
return;
|
||||
}
|
||||
|
||||
// A little header stuff.
|
||||
verbose.header('Initializing').writeflags(option.flags(), 'Command-line options');
|
||||
|
||||
// Determine and output which tasks will be run.
|
||||
var tasksSpecified = tasks && tasks.length > 0;
|
||||
tasks = task.parseArgs([tasksSpecified ? tasks : 'default']);
|
||||
|
||||
// Initialize tasks.
|
||||
task.init(tasks);
|
||||
|
||||
verbose.writeln();
|
||||
if (!tasksSpecified) {
|
||||
verbose.writeln('No tasks specified, running default tasks.');
|
||||
}
|
||||
verbose.writeflags(tasks, 'Running tasks');
|
||||
|
||||
// Handle otherwise unhandleable (probably asynchronous) exceptions.
|
||||
var uncaughtHandler = function(e) {
|
||||
fail.fatal(e, fail.code.TASK_FAILURE);
|
||||
};
|
||||
process.on('uncaughtException', uncaughtHandler);
|
||||
|
||||
// Report, etc when all tasks have completed.
|
||||
task.options({
|
||||
error: function(e) {
|
||||
fail.warn(e, fail.code.TASK_FAILURE);
|
||||
},
|
||||
done: function() {
|
||||
// Stop handling uncaught exceptions so that we don't leave any
|
||||
// unwanted process-level side effects behind. There is no need to do
|
||||
// this in the error callback, because fail.warn() will either kill
|
||||
// the process, or with --force keep on going all the way here.
|
||||
process.removeListener('uncaughtException', uncaughtHandler);
|
||||
|
||||
// Output a final fail / success report.
|
||||
fail.report();
|
||||
|
||||
if (done) {
|
||||
// Execute "done" function when done (only if passed, of course).
|
||||
done();
|
||||
} else {
|
||||
// Otherwise, explicitly exit.
|
||||
util.exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Execute all tasks, in order. Passing each task individually in a forEach
|
||||
// allows the error callback to execute multiple times.
|
||||
tasks.forEach(function(name) { task.run(name); });
|
||||
task.start();
|
||||
};
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// Nodejs libs.
|
||||
var path = require('path');
|
||||
|
||||
// External libs.
|
||||
var nopt = require('nopt');
|
||||
|
||||
// This is only executed when run via command line.
|
||||
var cli = module.exports = function(options, done) {
|
||||
// CLI-parsed options override any passed-in "default" options.
|
||||
if (options) {
|
||||
// For each defult option...
|
||||
Object.keys(options).forEach(function(key) {
|
||||
if (!(key in cli.options)) {
|
||||
// If this option doesn't exist in the parsed cli.options, add it in.
|
||||
cli.options[key] = options[key];
|
||||
} else if (cli.optlist[key].type === Array) {
|
||||
// If this option's type is Array, append it to any existing array
|
||||
// (or create a new array).
|
||||
[].push.apply(cli.options[key], options[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Run tasks.
|
||||
grunt.tasks(cli.tasks, cli.options, done);
|
||||
};
|
||||
|
||||
// Default options.
|
||||
var optlist = cli.optlist = {
|
||||
help: {
|
||||
short: 'h',
|
||||
info: 'Display this help text.',
|
||||
type: Boolean
|
||||
},
|
||||
base: {
|
||||
info: 'Specify an alternate base path. By default, all file paths are relative to the Gruntfile. (grunt.file.setBase) *',
|
||||
type: path
|
||||
},
|
||||
color: {
|
||||
info: 'Disable colored output.',
|
||||
type: Boolean,
|
||||
negate: true
|
||||
},
|
||||
gruntfile: {
|
||||
info: 'Specify an alternate Gruntfile. By default, grunt looks in the current or parent directories for the nearest Gruntfile.js or Gruntfile.coffee file.',
|
||||
type: path
|
||||
},
|
||||
debug: {
|
||||
short: 'd',
|
||||
info: 'Enable debugging mode for tasks that support it.',
|
||||
type: Number
|
||||
},
|
||||
stack: {
|
||||
info: 'Print a stack trace when exiting with a warning or fatal error.',
|
||||
type: Boolean
|
||||
},
|
||||
force: {
|
||||
short: 'f',
|
||||
info: 'A way to force your way past warnings. Want a suggestion? Don\'t use this option, fix your code.',
|
||||
type: Boolean
|
||||
},
|
||||
tasks: {
|
||||
info: 'Additional directory paths to scan for task and "extra" files. (grunt.loadTasks) *',
|
||||
type: Array
|
||||
},
|
||||
npm: {
|
||||
info: 'Npm-installed grunt plugins to scan for task and "extra" files. (grunt.loadNpmTasks) *',
|
||||
type: Array
|
||||
},
|
||||
write: {
|
||||
info: 'Disable writing files (dry run).',
|
||||
type: Boolean,
|
||||
negate: true
|
||||
},
|
||||
verbose: {
|
||||
short: 'v',
|
||||
info: 'Verbose mode. A lot more information output.',
|
||||
type: Boolean
|
||||
},
|
||||
version: {
|
||||
short: 'V',
|
||||
info: 'Print the grunt version. Combine with --verbose for more info.',
|
||||
type: Boolean
|
||||
},
|
||||
// Even though shell auto-completion is now handled by grunt-cli, leave this
|
||||
// option here for display in the --help screen.
|
||||
completion: {
|
||||
info: 'Output shell auto-completion rules. See the grunt-cli documentation for more information.',
|
||||
type: String
|
||||
},
|
||||
};
|
||||
|
||||
// Parse `optlist` into a form that nopt can handle.
|
||||
var aliases = {};
|
||||
var known = {};
|
||||
|
||||
Object.keys(optlist).forEach(function(key) {
|
||||
var short = optlist[key].short;
|
||||
if (short) {
|
||||
aliases[short] = '--' + key;
|
||||
}
|
||||
known[key] = optlist[key].type;
|
||||
});
|
||||
|
||||
var parsed = nopt(known, aliases, process.argv, 2);
|
||||
cli.tasks = parsed.argv.remain;
|
||||
cli.options = parsed;
|
||||
delete parsed.argv;
|
||||
|
||||
// Initialize any Array options that weren't initialized.
|
||||
Object.keys(optlist).forEach(function(key) {
|
||||
if (optlist[key].type === Array && !(key in cli.options)) {
|
||||
cli.options[key] = [];
|
||||
}
|
||||
});
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// Get/set config data. If value was passed, set. Otherwise, get.
|
||||
var config = module.exports = function(prop, value) {
|
||||
if (arguments.length === 2) {
|
||||
// Two arguments were passed, set the property's value.
|
||||
return config.set(prop, value);
|
||||
} else {
|
||||
// Get the property's value (or the entire data object).
|
||||
return config.get(prop);
|
||||
}
|
||||
};
|
||||
|
||||
// The actual config data.
|
||||
config.data = {};
|
||||
|
||||
// Escape any . in name with \. so dot-based namespacing works properly.
|
||||
config.escape = function(str) {
|
||||
return str.replace(/\./g, '\\.');
|
||||
};
|
||||
|
||||
// Return prop as a string.
|
||||
config.getPropString = function(prop) {
|
||||
return Array.isArray(prop) ? prop.map(config.escape).join('.') : prop;
|
||||
};
|
||||
|
||||
// Get raw, unprocessed config data.
|
||||
config.getRaw = function(prop) {
|
||||
if (prop) {
|
||||
// Prop was passed, get that specific property's value.
|
||||
return grunt.util.namespace.get(config.data, config.getPropString(prop));
|
||||
} else {
|
||||
// No prop was passed, return the entire config.data object.
|
||||
return config.data;
|
||||
}
|
||||
};
|
||||
|
||||
// Match '<%= FOO %>' where FOO is a propString, eg. foo or foo.bar but not
|
||||
// a method call like foo() or foo.bar().
|
||||
var propStringTmplRe = /^<%=\s*([a-z0-9_$]+(?:\.[a-z0-9_$]+)*)\s*%>$/i;
|
||||
|
||||
// Get config data, recursively processing templates.
|
||||
config.get = function(prop) {
|
||||
return config.process(config.getRaw(prop));
|
||||
};
|
||||
|
||||
// Expand a config value recursively. Used for post-processing raw values
|
||||
// already retrieved from the config.
|
||||
config.process = function(raw) {
|
||||
return grunt.util.recurse(raw, function(value) {
|
||||
// If the value is not a string, return it.
|
||||
if (typeof value !== 'string') { return value; }
|
||||
// If possible, access the specified property via config.get, in case it
|
||||
// doesn't refer to a string, but instead refers to an object or array.
|
||||
var matches = value.match(propStringTmplRe);
|
||||
var result;
|
||||
if (matches) {
|
||||
result = config.get(matches[1]);
|
||||
// If the result retrieved from the config data wasn't null or undefined,
|
||||
// return it.
|
||||
if (result != null) { return result; }
|
||||
}
|
||||
// Process the string as a template.
|
||||
return grunt.template.process(value, {data: config.data});
|
||||
});
|
||||
};
|
||||
|
||||
// Set config data.
|
||||
config.set = function(prop, value) {
|
||||
return grunt.util.namespace.set(config.data, config.getPropString(prop), value);
|
||||
};
|
||||
|
||||
// Initialize config data.
|
||||
config.init = function(obj) {
|
||||
grunt.verbose.write('Initializing config...').ok();
|
||||
// Initialize and return data.
|
||||
return (config.data = obj || {});
|
||||
};
|
||||
|
||||
// Test to see if required config params have been defined. If not, throw an
|
||||
// exception (use this inside of a task).
|
||||
config.requires = function() {
|
||||
var p = grunt.util.pluralize;
|
||||
var props = grunt.util.toArray(arguments).map(config.getPropString);
|
||||
var msg = 'Verifying propert' + p(props.length, 'y/ies') +
|
||||
' ' + grunt.log.wordlist(props) + ' exist' + p(props.length, 's') +
|
||||
' in config...';
|
||||
grunt.verbose.write(msg);
|
||||
var failProps = config.data && props.filter(function(prop) {
|
||||
return config.get(prop) == null;
|
||||
}).map(function(prop) {
|
||||
return '"' + prop + '"';
|
||||
});
|
||||
if (config.data && failProps.length === 0) {
|
||||
grunt.verbose.ok();
|
||||
return true;
|
||||
} else {
|
||||
grunt.verbose.or.write(msg);
|
||||
grunt.log.error().error('Unable to process task.');
|
||||
if (!config.data) {
|
||||
throw grunt.util.error('Unable to load config.');
|
||||
} else {
|
||||
throw grunt.util.error('Required config propert' +
|
||||
p(failProps.length, 'y/ies') + ' ' + failProps.join(', ') + ' missing.');
|
||||
}
|
||||
}
|
||||
};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// External lib.
|
||||
var EventEmitter2 = require('eventemitter2').EventEmitter2;
|
||||
|
||||
// Awesome.
|
||||
module.exports = new EventEmitter2({wildcard: true});
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// The module to be exported.
|
||||
var fail = module.exports = {};
|
||||
|
||||
// Error codes.
|
||||
fail.code = {
|
||||
FATAL_ERROR: 1,
|
||||
MISSING_GRUNTFILE: 2,
|
||||
TASK_FAILURE: 3,
|
||||
TEMPLATE_ERROR: 4,
|
||||
INVALID_AUTOCOMPLETE: 5,
|
||||
WARNING: 6,
|
||||
};
|
||||
|
||||
// DRY it up!
|
||||
function writeln(e, mode) {
|
||||
grunt.log.muted = false;
|
||||
var msg = String(e.message || e);
|
||||
if (!grunt.option('no-color')) { msg += '\x07'; } // Beep!
|
||||
if (mode === 'warn') {
|
||||
msg = 'Warning: ' + msg + ' ';
|
||||
msg += (grunt.option('force') ? 'Used --force, continuing.'.underline : 'Use --force to continue.');
|
||||
msg = msg.yellow;
|
||||
} else {
|
||||
msg = ('Fatal error: ' + msg).red;
|
||||
}
|
||||
grunt.log.writeln(msg);
|
||||
}
|
||||
|
||||
// If --stack is enabled, log the appropriate error stack (if it exists).
|
||||
function dumpStack(e) {
|
||||
if (grunt.option('stack')) {
|
||||
if (e.origError && e.origError.stack) {
|
||||
console.log(e.origError.stack);
|
||||
} else if (e.stack) {
|
||||
console.log(e.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A fatal error occured. Abort immediately.
|
||||
fail.fatal = function(e, errcode) {
|
||||
writeln(e, 'fatal');
|
||||
dumpStack(e);
|
||||
process.exit(typeof errcode === 'number' ? errcode : fail.code.FATAL_ERROR);
|
||||
};
|
||||
|
||||
// Keep track of error and warning counts.
|
||||
fail.errorcount = 0;
|
||||
fail.warncount = 0;
|
||||
|
||||
// A warning ocurred. Abort immediately unless -f or --force was used.
|
||||
fail.warn = function(e, errcode) {
|
||||
var message = typeof e === 'string' ? e : e.message;
|
||||
fail.warncount++;
|
||||
writeln(message, 'warn');
|
||||
// If -f or --force aren't used, stop script processing.
|
||||
if (!grunt.option('force')) {
|
||||
dumpStack(e);
|
||||
grunt.log.writeln().fail('Aborted due to warnings.');
|
||||
process.exit(typeof errcode === 'number' ? errcode : fail.code.WARNING);
|
||||
}
|
||||
};
|
||||
|
||||
// This gets called at the very end.
|
||||
fail.report = function() {
|
||||
if (fail.warncount > 0) {
|
||||
grunt.log.writeln().fail('Done, but with warnings.');
|
||||
} else {
|
||||
grunt.log.writeln().success('Done, without errors.');
|
||||
}
|
||||
};
|
||||
+438
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// Nodejs libs.
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// The module to be exported.
|
||||
var file = module.exports = {};
|
||||
|
||||
// External libs.
|
||||
file.glob = require('glob');
|
||||
file.minimatch = require('minimatch');
|
||||
file.findup = require('findup-sync');
|
||||
var YAML = require('js-yaml');
|
||||
var rimraf = require('rimraf');
|
||||
var iconv = require('iconv-lite');
|
||||
|
||||
// Windows?
|
||||
var win32 = process.platform === 'win32';
|
||||
|
||||
// Normalize \\ paths to / paths.
|
||||
var unixifyPath = function(filepath) {
|
||||
if (win32) {
|
||||
return filepath.replace(/\\/g, '/');
|
||||
} else {
|
||||
return filepath;
|
||||
}
|
||||
};
|
||||
|
||||
// Change the current base path (ie, CWD) to the specified path.
|
||||
file.setBase = function() {
|
||||
var dirpath = path.join.apply(path, arguments);
|
||||
process.chdir(dirpath);
|
||||
};
|
||||
|
||||
// Process specified wildcard glob patterns or filenames against a
|
||||
// callback, excluding and uniquing files in the result set.
|
||||
var processPatterns = function(patterns, fn) {
|
||||
// Filepaths to return.
|
||||
var result = [];
|
||||
// Iterate over flattened patterns array.
|
||||
grunt.util._.flatten(patterns).forEach(function(pattern) {
|
||||
// If the first character is ! it should be omitted
|
||||
var exclusion = pattern.indexOf('!') === 0;
|
||||
// If the pattern is an exclusion, remove the !
|
||||
if (exclusion) { pattern = pattern.slice(1); }
|
||||
// Find all matching files for this pattern.
|
||||
var matches = fn(pattern);
|
||||
if (exclusion) {
|
||||
// If an exclusion, remove matching files.
|
||||
result = grunt.util._.difference(result, matches);
|
||||
} else {
|
||||
// Otherwise add matching files.
|
||||
result = grunt.util._.union(result, matches);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Match a filepath or filepaths against one or more wildcard patterns. Returns
|
||||
// all matching filepaths.
|
||||
file.match = function(options, patterns, filepaths) {
|
||||
if (grunt.util.kindOf(options) !== 'object') {
|
||||
filepaths = patterns;
|
||||
patterns = options;
|
||||
options = {};
|
||||
}
|
||||
// Return empty set if either patterns or filepaths was omitted.
|
||||
if (patterns == null || filepaths == null) { return []; }
|
||||
// Normalize patterns and filepaths to arrays.
|
||||
if (!Array.isArray(patterns)) { patterns = [patterns]; }
|
||||
if (!Array.isArray(filepaths)) { filepaths = [filepaths]; }
|
||||
// Return empty set if there are no patterns or filepaths.
|
||||
if (patterns.length === 0 || filepaths.length === 0) { return []; }
|
||||
// Return all matching filepaths.
|
||||
return processPatterns(patterns, function(pattern) {
|
||||
return file.minimatch.match(filepaths, pattern, options);
|
||||
});
|
||||
};
|
||||
|
||||
// Match a filepath or filepaths against one or more wildcard patterns. Returns
|
||||
// true if any of the patterns match.
|
||||
file.isMatch = function() {
|
||||
return file.match.apply(file, arguments).length > 0;
|
||||
};
|
||||
|
||||
// Return an array of all file paths that match the given wildcard patterns.
|
||||
file.expand = function() {
|
||||
var args = grunt.util.toArray(arguments);
|
||||
// If the first argument is an options object, save those options to pass
|
||||
// into the file.glob.sync method.
|
||||
var options = grunt.util.kindOf(args[0]) === 'object' ? args.shift() : {};
|
||||
// Use the first argument if it's an Array, otherwise convert the arguments
|
||||
// object to an array and use that.
|
||||
var patterns = Array.isArray(args[0]) ? args[0] : args;
|
||||
// Return empty set if there are no patterns or filepaths.
|
||||
if (patterns.length === 0) { return []; }
|
||||
// Return all matching filepaths.
|
||||
var matches = processPatterns(patterns, function(pattern) {
|
||||
// Find all matching files for this pattern.
|
||||
return file.glob.sync(pattern, options);
|
||||
});
|
||||
// Filter result set?
|
||||
if (options.filter) {
|
||||
matches = matches.filter(function(filepath) {
|
||||
filepath = path.join(options.cwd || '', filepath);
|
||||
try {
|
||||
if (typeof options.filter === 'function') {
|
||||
return options.filter(filepath);
|
||||
} else {
|
||||
// If the file is of the right type and exists, this should work.
|
||||
return fs.statSync(filepath)[options.filter]();
|
||||
}
|
||||
} catch(e) {
|
||||
// Otherwise, it's probably not the right type.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
|
||||
var pathSeparatorRe = /[\/\\]/g;
|
||||
|
||||
// Build a multi task "files" object dynamically.
|
||||
file.expandMapping = function(patterns, destBase, options) {
|
||||
options = grunt.util._.defaults({}, options, {
|
||||
rename: function(destBase, destPath) {
|
||||
return path.join(destBase || '', destPath);
|
||||
}
|
||||
});
|
||||
var files = [];
|
||||
var fileByDest = {};
|
||||
// Find all files matching pattern, using passed-in options.
|
||||
file.expand(options, patterns).forEach(function(src) {
|
||||
var destPath = src;
|
||||
// Flatten?
|
||||
if (options.flatten) {
|
||||
destPath = path.basename(destPath);
|
||||
}
|
||||
// Change the extension?
|
||||
if (options.ext) {
|
||||
destPath = destPath.replace(/(\.[^\/]*)?$/, options.ext);
|
||||
}
|
||||
// Generate destination filename.
|
||||
var dest = options.rename(destBase, destPath, options);
|
||||
// Prepend cwd to src path if necessary.
|
||||
if (options.cwd) { src = path.join(options.cwd, src); }
|
||||
// Normalize filepaths to be unix-style.
|
||||
dest = dest.replace(pathSeparatorRe, '/');
|
||||
src = src.replace(pathSeparatorRe, '/');
|
||||
// Map correct src path to dest path.
|
||||
if (fileByDest[dest]) {
|
||||
// If dest already exists, push this src onto that dest's src array.
|
||||
fileByDest[dest].src.push(src);
|
||||
} else {
|
||||
// Otherwise create a new src-dest file mapping object.
|
||||
files.push({
|
||||
src: [src],
|
||||
dest: dest,
|
||||
});
|
||||
// And store a reference for later use.
|
||||
fileByDest[dest] = files[files.length - 1];
|
||||
}
|
||||
});
|
||||
return files;
|
||||
};
|
||||
|
||||
// Like mkdir -p. Create a directory and any intermediary directories.
|
||||
file.mkdir = function(dirpath, mode) {
|
||||
if (grunt.option('no-write')) { return; }
|
||||
// Set directory mode in a strict-mode-friendly way.
|
||||
if (mode == null) {
|
||||
mode = parseInt('0777', 8) & (~process.umask());
|
||||
}
|
||||
dirpath.split(pathSeparatorRe).reduce(function(parts, part) {
|
||||
parts += part + '/';
|
||||
var subpath = path.resolve(parts);
|
||||
if (!file.exists(subpath)) {
|
||||
try {
|
||||
fs.mkdirSync(subpath, mode);
|
||||
} catch(e) {
|
||||
throw grunt.util.error('Unable to create directory "' + subpath + '" (Error code: ' + e.code + ').', e);
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}, '');
|
||||
};
|
||||
|
||||
// Recurse into a directory, executing callback for each file.
|
||||
file.recurse = function recurse(rootdir, callback, subdir) {
|
||||
var abspath = subdir ? path.join(rootdir, subdir) : rootdir;
|
||||
fs.readdirSync(abspath).forEach(function(filename) {
|
||||
var filepath = path.join(abspath, filename);
|
||||
if (fs.statSync(filepath).isDirectory()) {
|
||||
recurse(rootdir, callback, unixifyPath(path.join(subdir || '', filename || '')));
|
||||
} else {
|
||||
callback(unixifyPath(filepath), rootdir, subdir, filename);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// The default file encoding to use.
|
||||
file.defaultEncoding = 'utf8';
|
||||
|
||||
// Read a file, return its contents.
|
||||
file.read = function(filepath, options) {
|
||||
if (!options) { options = {}; }
|
||||
var contents;
|
||||
grunt.verbose.write('Reading ' + filepath + '...');
|
||||
try {
|
||||
contents = fs.readFileSync(String(filepath));
|
||||
// If encoding is not explicitly null, convert from encoded buffer to a
|
||||
// string. If no encoding was specified, use the default.
|
||||
if (options.encoding !== null) {
|
||||
contents = iconv.decode(contents, options.encoding || file.defaultEncoding);
|
||||
// Strip any BOM that might exist.
|
||||
if (contents.charCodeAt(0) === 0xFEFF) {
|
||||
contents = contents.substring(1);
|
||||
}
|
||||
}
|
||||
grunt.verbose.ok();
|
||||
return contents;
|
||||
} catch(e) {
|
||||
grunt.verbose.error();
|
||||
throw grunt.util.error('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Read a file, parse its contents, return an object.
|
||||
file.readJSON = function(filepath, options) {
|
||||
var src = file.read(filepath, options);
|
||||
var result;
|
||||
grunt.verbose.write('Parsing ' + filepath + '...');
|
||||
try {
|
||||
result = JSON.parse(src);
|
||||
grunt.verbose.ok();
|
||||
return result;
|
||||
} catch(e) {
|
||||
grunt.verbose.error();
|
||||
throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Read a YAML file, parse its contents, return an object.
|
||||
file.readYAML = function(filepath, options) {
|
||||
var src = file.read(filepath, options);
|
||||
var result;
|
||||
grunt.verbose.write('Parsing ' + filepath + '...');
|
||||
try {
|
||||
result = YAML.load(src);
|
||||
grunt.verbose.ok();
|
||||
return result;
|
||||
} catch(e) {
|
||||
grunt.verbose.error();
|
||||
throw grunt.util.error('Unable to parse "' + filepath + '" file (' + e.problem + ').', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Write a file.
|
||||
file.write = function(filepath, contents, options) {
|
||||
if (!options) { options = {}; }
|
||||
var nowrite = grunt.option('no-write');
|
||||
grunt.verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...');
|
||||
// Create path, if necessary.
|
||||
file.mkdir(path.dirname(filepath));
|
||||
try {
|
||||
// If contents is already a Buffer, don't try to encode it. If no encoding
|
||||
// was specified, use the default.
|
||||
if (!Buffer.isBuffer(contents)) {
|
||||
contents = iconv.encode(contents, options.encoding || file.defaultEncoding);
|
||||
}
|
||||
// Actually write file.
|
||||
if (!nowrite) {
|
||||
fs.writeFileSync(filepath, contents);
|
||||
}
|
||||
grunt.verbose.ok();
|
||||
return true;
|
||||
} catch(e) {
|
||||
grunt.verbose.error();
|
||||
throw grunt.util.error('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Read a file, optionally processing its content, then write the output.
|
||||
file.copy = function(srcpath, destpath, options) {
|
||||
if (!options) { options = {}; }
|
||||
// If a process function was specified, and noProcess isn't true or doesn't
|
||||
// match the srcpath, process the file's source.
|
||||
var process = options.process && options.noProcess !== true &&
|
||||
!(options.noProcess && file.isMatch(options.noProcess, srcpath));
|
||||
// If the file will be processed, use the encoding as-specified. Otherwise,
|
||||
// use an encoding of null to force the file to be read/written as a Buffer.
|
||||
var readWriteOptions = process ? options : {encoding: null};
|
||||
// Actually read the file.
|
||||
var contents = file.read(srcpath, readWriteOptions);
|
||||
if (process) {
|
||||
grunt.verbose.write('Processing source...');
|
||||
try {
|
||||
contents = options.process(contents, srcpath);
|
||||
grunt.verbose.ok();
|
||||
} catch(e) {
|
||||
grunt.verbose.error();
|
||||
throw grunt.util.error('Error while processing "' + srcpath + '" file.', e);
|
||||
}
|
||||
}
|
||||
// Abort copy if the process function returns false.
|
||||
if (contents === false) {
|
||||
grunt.verbose.writeln('Write aborted.');
|
||||
} else {
|
||||
file.write(destpath, contents, readWriteOptions);
|
||||
}
|
||||
};
|
||||
|
||||
// Delete folders and files recursively
|
||||
file.delete = function(filepath, options) {
|
||||
filepath = String(filepath);
|
||||
|
||||
var nowrite = grunt.option('no-write');
|
||||
if (!options) {
|
||||
options = {force: grunt.option('force') || false};
|
||||
}
|
||||
|
||||
grunt.verbose.write((nowrite ? 'Not actually deleting ' : 'Deleting ') + filepath + '...');
|
||||
|
||||
if (!file.exists(filepath)) {
|
||||
grunt.verbose.error();
|
||||
grunt.log.warn('Cannot delete nonexistent file.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only delete cwd or outside cwd if --force enabled. Be careful, people!
|
||||
if (!options.force) {
|
||||
if (file.isPathCwd(filepath)) {
|
||||
grunt.verbose.error();
|
||||
grunt.fail.warn('Cannot delete the current working directory.');
|
||||
return false;
|
||||
} else if (!file.isPathInCwd(filepath)) {
|
||||
grunt.verbose.error();
|
||||
grunt.fail.warn('Cannot delete files outside the current working directory.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Actually delete. Or not.
|
||||
if (!nowrite) {
|
||||
rimraf.sync(filepath);
|
||||
}
|
||||
grunt.verbose.ok();
|
||||
return true;
|
||||
} catch(e) {
|
||||
grunt.verbose.error();
|
||||
throw grunt.util.error('Unable to delete "' + filepath + '" file (' + e.message + ').', e);
|
||||
}
|
||||
};
|
||||
|
||||
// True if the file path exists.
|
||||
file.exists = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
return fs.existsSync(filepath);
|
||||
};
|
||||
|
||||
// True if the file is a symbolic link.
|
||||
file.isLink = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
return file.exists(filepath) && fs.lstatSync(filepath).isSymbolicLink();
|
||||
};
|
||||
|
||||
// True if the path is a directory.
|
||||
file.isDir = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
return file.exists(filepath) && fs.statSync(filepath).isDirectory();
|
||||
};
|
||||
|
||||
// True if the path is a file.
|
||||
file.isFile = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
return file.exists(filepath) && fs.statSync(filepath).isFile();
|
||||
};
|
||||
|
||||
// Is a given file path absolute?
|
||||
file.isPathAbsolute = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
return path.resolve(filepath) === filepath.replace(/[\/\\]+$/, '');
|
||||
};
|
||||
|
||||
// Do all the specified paths refer to the same path?
|
||||
file.arePathsEquivalent = function(first) {
|
||||
first = path.resolve(first);
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
if (first !== path.resolve(arguments[i])) { return false; }
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Are descendant path(s) contained within ancestor path? Note: does not test
|
||||
// if paths actually exist.
|
||||
file.doesPathContain = function(ancestor) {
|
||||
ancestor = path.resolve(ancestor);
|
||||
var relative;
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
relative = path.relative(path.resolve(arguments[i]), ancestor);
|
||||
if (relative === '' || /\w+/.test(relative)) { return false; }
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Test to see if a filepath is the CWD.
|
||||
file.isPathCwd = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
try {
|
||||
return file.arePathsEquivalent(process.cwd(), fs.realpathSync(filepath));
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Test to see if a filepath is contained within the CWD.
|
||||
file.isPathInCwd = function() {
|
||||
var filepath = path.join.apply(path, arguments);
|
||||
try {
|
||||
return file.doesPathContain(process.cwd(), fs.realpathSync(filepath));
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// Nodejs libs.
|
||||
var path = require('path');
|
||||
|
||||
// Set column widths.
|
||||
var col1len = 0;
|
||||
exports.initCol1 = function(str) {
|
||||
col1len = Math.max(col1len, str.length);
|
||||
};
|
||||
exports.initWidths = function() {
|
||||
// Widths for options/tasks table output.
|
||||
exports.widths = [1, col1len, 2, 76 - col1len];
|
||||
};
|
||||
|
||||
// Render an array in table form.
|
||||
exports.table = function(arr) {
|
||||
arr.forEach(function(item) {
|
||||
grunt.log.writetableln(exports.widths, ['', grunt.util._.pad(item[0], col1len), '', item[1]]);
|
||||
});
|
||||
};
|
||||
|
||||
// Methods to run, in-order.
|
||||
exports.queue = [
|
||||
'initOptions',
|
||||
'initTasks',
|
||||
'initWidths',
|
||||
'header',
|
||||
'usage',
|
||||
'options',
|
||||
'optionsFooter',
|
||||
'tasks',
|
||||
'footer',
|
||||
];
|
||||
|
||||
// Actually display stuff.
|
||||
exports.display = function() {
|
||||
exports.queue.forEach(function(name) { exports[name](); });
|
||||
};
|
||||
|
||||
|
||||
// Header.
|
||||
exports.header = function() {
|
||||
grunt.log.writeln('Grunt: The JavaScript Task Runner (v' + grunt.version + ')');
|
||||
};
|
||||
|
||||
// Usage info.
|
||||
exports.usage = function() {
|
||||
grunt.log.header('Usage');
|
||||
grunt.log.writeln(' ' + path.basename(process.argv[1]) + ' [options] [task [task ...]]');
|
||||
};
|
||||
|
||||
// Options.
|
||||
exports.initOptions = function() {
|
||||
// Build 2-column array for table view.
|
||||
exports._options = Object.keys(grunt.cli.optlist).map(function(long) {
|
||||
var o = grunt.cli.optlist[long];
|
||||
var col1 = '--' + (o.negate ? 'no-' : '') + long + (o.short ? ', -' + o.short : '');
|
||||
exports.initCol1(col1);
|
||||
return [col1, o.info];
|
||||
});
|
||||
};
|
||||
|
||||
exports.options = function() {
|
||||
grunt.log.header('Options');
|
||||
exports.table(exports._options);
|
||||
};
|
||||
|
||||
exports.optionsFooter = function() {
|
||||
grunt.log.writeln().writelns(
|
||||
'Options marked with * have methods exposed via the grunt API and should ' +
|
||||
'instead be specified inside the Gruntfile wherever possible.'
|
||||
);
|
||||
};
|
||||
|
||||
// Tasks.
|
||||
exports.initTasks = function() {
|
||||
// Initialize task system so that the tasks can be listed.
|
||||
grunt.task.init([], {help: true});
|
||||
|
||||
// Build object of tasks by info (where they were loaded from).
|
||||
exports._tasks = [];
|
||||
Object.keys(grunt.task._tasks).forEach(function(name) {
|
||||
exports.initCol1(name);
|
||||
var task = grunt.task._tasks[name];
|
||||
exports._tasks.push(task);
|
||||
});
|
||||
};
|
||||
|
||||
exports.tasks = function() {
|
||||
grunt.log.header('Available tasks');
|
||||
if (exports._tasks.length === 0) {
|
||||
grunt.log.writeln('(no tasks found)');
|
||||
} else {
|
||||
exports.table(exports._tasks.map(function(task) {
|
||||
var info = task.info;
|
||||
if (task.multi) { info += ' *'; }
|
||||
return [task.name, info];
|
||||
}));
|
||||
|
||||
grunt.log.writeln().writelns(
|
||||
'Tasks run in the order specified. Arguments may be passed to tasks that ' +
|
||||
'accept them by using colons, like "lint:files". Tasks marked with * are ' +
|
||||
'"multi tasks" and will iterate over all sub-targets if no argument is ' +
|
||||
'specified.'
|
||||
);
|
||||
}
|
||||
|
||||
grunt.log.writeln().writelns(
|
||||
'The list of available tasks may change based on tasks directories or ' +
|
||||
'grunt plugins specified in the Gruntfile or via command-line options.'
|
||||
);
|
||||
};
|
||||
|
||||
// Footer.
|
||||
exports.footer = function() {
|
||||
grunt.log.writeln().writeln('For more information, see http://gruntjs.com/');
|
||||
};
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// Nodejs libs.
|
||||
var util = require('util');
|
||||
|
||||
// The module to be exported.
|
||||
var log = module.exports = {};
|
||||
|
||||
// External lib. Requiring this here modifies the String prototype!
|
||||
var colors = require('colors');
|
||||
|
||||
// Disable colors if --no-colors was passed.
|
||||
log.initColors = function() {
|
||||
var util = grunt.util;
|
||||
if (grunt.option('no-color')) {
|
||||
// String color getters should just return the string.
|
||||
colors.mode = 'none';
|
||||
// Strip colors from strings passed to console.log.
|
||||
util.hooker.hook(console, 'log', function() {
|
||||
var args = util.toArray(arguments);
|
||||
return util.hooker.filter(this, args.map(function(arg) {
|
||||
return util.kindOf(arg) === 'string' ? colors.stripColors(arg) : arg;
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Temporarily suppress output.
|
||||
var suppressOutput;
|
||||
|
||||
// Allow external muting of output.
|
||||
log.muted = false;
|
||||
|
||||
// True once anything has actually been logged.
|
||||
var hasLogged;
|
||||
|
||||
// Parse certain markup in strings to be logged.
|
||||
function markup(str) {
|
||||
str = str || '';
|
||||
// Make _foo_ underline.
|
||||
str = str.replace(/(\s|^)_(\S|\S[\s\S]+?\S)_(?=[\s,.!?]|$)/g, '$1' + '$2'.underline);
|
||||
// Make *foo* bold.
|
||||
str = str.replace(/(\s|^)\*(\S|\S[\s\S]+?\S)\*(?=[\s,.!?]|$)/g, '$1' + '$2'.bold);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Similar to util.format in the standard library, however it'll always
|
||||
// cast the first argument to a string and treat it as the format string.
|
||||
function format(args) {
|
||||
// Args is a argument array so copy it in order to avoid wonky behavior.
|
||||
args = [].slice.call(args, 0);
|
||||
if (args.length > 0) {
|
||||
args[0] = String(args[0]);
|
||||
}
|
||||
return util.format.apply(util, args);
|
||||
}
|
||||
|
||||
function write(msg) {
|
||||
msg = msg || '';
|
||||
// Actually write output.
|
||||
if (!log.muted && !suppressOutput) {
|
||||
hasLogged = true;
|
||||
// Users should probably use the colors-provided methods, but if they
|
||||
// don't, this should strip extraneous color codes.
|
||||
if (grunt.option('no-color')) { msg = colors.stripColors(msg); }
|
||||
// Actually write to stdout.
|
||||
process.stdout.write(markup(msg));
|
||||
}
|
||||
}
|
||||
|
||||
function writeln(msg) {
|
||||
// Write blank line if no msg is passed in.
|
||||
msg = msg || '';
|
||||
write(msg + '\n');
|
||||
}
|
||||
|
||||
// Write output.
|
||||
log.write = function() {
|
||||
write(format(arguments));
|
||||
return log;
|
||||
};
|
||||
|
||||
// Write a line of output.
|
||||
log.writeln = function() {
|
||||
writeln(format(arguments));
|
||||
return log;
|
||||
};
|
||||
|
||||
log.warn = function() {
|
||||
var msg = format(arguments);
|
||||
if (arguments.length > 0) {
|
||||
writeln('>> '.red + grunt.util._.trim(msg).replace(/\n/g, '\n>> '.red));
|
||||
} else {
|
||||
writeln('ERROR'.red);
|
||||
}
|
||||
return log;
|
||||
};
|
||||
log.error = function() {
|
||||
grunt.fail.errorcount++;
|
||||
log.warn.apply(log, arguments);
|
||||
return log;
|
||||
};
|
||||
log.ok = function() {
|
||||
var msg = format(arguments);
|
||||
if (arguments.length > 0) {
|
||||
writeln('>> '.green + grunt.util._.trim(msg).replace(/\n/g, '\n>> '.green));
|
||||
} else {
|
||||
writeln('OK'.green);
|
||||
}
|
||||
return log;
|
||||
};
|
||||
log.errorlns = function() {
|
||||
var msg = format(arguments);
|
||||
log.error(log.wraptext(77, msg));
|
||||
return log;
|
||||
};
|
||||
log.oklns = function() {
|
||||
var msg = format(arguments);
|
||||
log.ok(log.wraptext(77, msg));
|
||||
return log;
|
||||
};
|
||||
log.success = function() {
|
||||
var msg = format(arguments);
|
||||
writeln(msg.green);
|
||||
return log;
|
||||
};
|
||||
log.fail = function() {
|
||||
var msg = format(arguments);
|
||||
writeln(msg.red);
|
||||
return log;
|
||||
};
|
||||
log.header = function() {
|
||||
var msg = format(arguments);
|
||||
// Skip line before header, but not if header is the very first line output.
|
||||
if (hasLogged) { writeln(); }
|
||||
writeln(msg.underline);
|
||||
return log;
|
||||
};
|
||||
log.subhead = function() {
|
||||
var msg = format(arguments);
|
||||
// Skip line before subhead, but not if subhead is the very first line output.
|
||||
if (hasLogged) { writeln(); }
|
||||
writeln(msg.bold);
|
||||
return log;
|
||||
};
|
||||
// For debugging.
|
||||
log.debug = function() {
|
||||
var msg = format(arguments);
|
||||
if (grunt.option('debug')) {
|
||||
writeln('[D] ' + msg.magenta);
|
||||
}
|
||||
return log;
|
||||
};
|
||||
|
||||
// Write a line of a table.
|
||||
log.writetableln = function(widths, texts) {
|
||||
writeln(log.table(widths, texts));
|
||||
return log;
|
||||
};
|
||||
|
||||
// Wrap a long line of text to 80 columns.
|
||||
log.writelns = function() {
|
||||
var msg = format(arguments);
|
||||
writeln(log.wraptext(80, msg));
|
||||
return log;
|
||||
};
|
||||
|
||||
// Display flags in verbose mode.
|
||||
log.writeflags = function(obj, prefix) {
|
||||
var wordlist;
|
||||
if (Array.isArray(obj)) {
|
||||
wordlist = log.wordlist(obj);
|
||||
} else if (typeof obj === 'object' && obj) {
|
||||
wordlist = log.wordlist(Object.keys(obj).map(function(key) {
|
||||
var val = obj[key];
|
||||
return key + (val === true ? '' : '=' + JSON.stringify(val));
|
||||
}));
|
||||
}
|
||||
writeln((prefix || 'Flags') + ': ' + (wordlist || '(none)'.cyan));
|
||||
return log;
|
||||
};
|
||||
|
||||
// Create explicit "verbose" and "notverbose" functions, one for each already-
|
||||
// defined log function, that do the same thing but ONLY if -v or --verbose is
|
||||
// specified (or not specified).
|
||||
log.verbose = {};
|
||||
log.notverbose = {};
|
||||
|
||||
// Iterate over all exported functions.
|
||||
Object.keys(log).filter(function(key) {
|
||||
return typeof log[key] === 'function';
|
||||
}).forEach(function(key) {
|
||||
// Like any other log function, but suppresses output if the "verbose" option
|
||||
// IS NOT set.
|
||||
log.verbose[key] = function() {
|
||||
suppressOutput = !grunt.option('verbose');
|
||||
log[key].apply(log, arguments);
|
||||
suppressOutput = false;
|
||||
return log.verbose;
|
||||
};
|
||||
// Like any other log function, but suppresses output if the "verbose" option
|
||||
// IS set.
|
||||
log.notverbose[key] = function() {
|
||||
suppressOutput = grunt.option('verbose');
|
||||
log[key].apply(log, arguments);
|
||||
suppressOutput = false;
|
||||
return log.notverbose;
|
||||
};
|
||||
});
|
||||
|
||||
// A way to switch between verbose and notverbose modes. For example, this will
|
||||
// write 'foo' if verbose logging is enabled, otherwise write 'bar':
|
||||
// verbose.write('foo').or.write('bar');
|
||||
log.verbose.or = log.notverbose;
|
||||
log.notverbose.or = log.verbose;
|
||||
|
||||
// Static methods.
|
||||
|
||||
// Pretty-format a word list.
|
||||
log.wordlist = function(arr, options) {
|
||||
options = grunt.util._.defaults(options || {}, {
|
||||
separator: ', ',
|
||||
color: 'cyan'
|
||||
});
|
||||
return arr.map(function(item) {
|
||||
return options.color ? String(item)[options.color] : item;
|
||||
}).join(options.separator);
|
||||
};
|
||||
|
||||
// Return a string, uncolored (suitable for testing .length, etc).
|
||||
log.uncolor = function(str) {
|
||||
return str.replace(/\x1B\[\d+m/g, '');
|
||||
};
|
||||
|
||||
// Word-wrap text to a given width, permitting ANSI color codes.
|
||||
log.wraptext = function(width, text) {
|
||||
// notes to self:
|
||||
// grab 1st character or ansi code from string
|
||||
// if ansi code, add to array and save for later, strip from front of string
|
||||
// if character, add to array and increment counter, strip from front of string
|
||||
// if width + 1 is reached and current character isn't space:
|
||||
// slice off everything after last space in array and prepend it to string
|
||||
// etc
|
||||
|
||||
// This result array will be joined on \n.
|
||||
var result = [];
|
||||
var matches, color, tmp;
|
||||
var captured = [];
|
||||
var charlen = 0;
|
||||
|
||||
while (matches = text.match(/(?:(\x1B\[\d+m)|\n|(.))([\s\S]*)/)) {
|
||||
// Updated text to be everything not matched.
|
||||
text = matches[3];
|
||||
|
||||
// Matched a color code?
|
||||
if (matches[1]) {
|
||||
// Save last captured color code for later use.
|
||||
color = matches[1];
|
||||
// Capture color code.
|
||||
captured.push(matches[1]);
|
||||
continue;
|
||||
|
||||
// Matched a non-newline character?
|
||||
} else if (matches[2]) {
|
||||
// If this is the first character and a previous color code was set, push
|
||||
// that onto the captured array first.
|
||||
if (charlen === 0 && color) { captured.push(color); }
|
||||
// Push the matched character.
|
||||
captured.push(matches[2]);
|
||||
// Increment the current charlen.
|
||||
charlen++;
|
||||
// If not yet at the width limit or a space was matched, continue.
|
||||
if (charlen <= width || matches[2] === ' ') { continue; }
|
||||
// The current charlen exceeds the width and a space wasn't matched.
|
||||
// "Roll everything back" until the last space character.
|
||||
tmp = captured.lastIndexOf(' ');
|
||||
text = captured.slice(tmp === -1 ? tmp : tmp + 1).join('') + text;
|
||||
captured = captured.slice(0, tmp);
|
||||
}
|
||||
|
||||
// The limit has been reached. Push captured string onto result array.
|
||||
result.push(captured.join(''));
|
||||
|
||||
// Reset captured array and charlen.
|
||||
captured = [];
|
||||
charlen = 0;
|
||||
}
|
||||
|
||||
result.push(captured.join(''));
|
||||
return result.join('\n');
|
||||
};
|
||||
|
||||
// todo: write unit tests
|
||||
//
|
||||
// function logs(text) {
|
||||
// [4, 6, 10, 15, 20, 25, 30, 40].forEach(function(n) {
|
||||
// log(n, text);
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// function log(n, text) {
|
||||
// console.log(Array(n + 1).join('-'));
|
||||
// console.log(wrap(n, text));
|
||||
// }
|
||||
//
|
||||
// var text = 'this is '.red + 'a simple'.yellow.inverse + ' test of'.green + ' ' + 'some wrapped'.blue + ' text over '.inverse.magenta + 'many lines'.red;
|
||||
// logs(text);
|
||||
//
|
||||
// var text = 'foolish '.red.inverse + 'monkeys'.yellow + ' eating'.green + ' ' + 'delicious'.inverse.blue + ' bananas '.magenta + 'forever'.red;
|
||||
// logs(text);
|
||||
//
|
||||
// var text = 'foolish monkeys eating delicious bananas forever'.rainbow;
|
||||
// logs(text);
|
||||
|
||||
// Format output into columns, wrapping words as-necessary.
|
||||
log.table = function(widths, texts) {
|
||||
var rows = [];
|
||||
widths.forEach(function(width, i) {
|
||||
var lines = log.wraptext(width, texts[i]).split('\n');
|
||||
lines.forEach(function(line, j) {
|
||||
var row = rows[j];
|
||||
if (!row) { row = rows[j] = []; }
|
||||
row[i] = line;
|
||||
});
|
||||
});
|
||||
|
||||
var lines = [];
|
||||
rows.forEach(function(row) {
|
||||
var txt = '';
|
||||
var column;
|
||||
for (var i = 0; i < row.length; i++) {
|
||||
column = row[i] || '';
|
||||
txt += column;
|
||||
var diff = widths[i] - log.uncolor(column).length;
|
||||
if (diff > 0) { txt += grunt.util.repeat(diff, ' '); }
|
||||
}
|
||||
lines.push(txt);
|
||||
});
|
||||
|
||||
return lines.join('\n');
|
||||
};
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// The actual option data.
|
||||
var data = {};
|
||||
|
||||
// Get or set an option value.
|
||||
var option = module.exports = function(key, value) {
|
||||
var no = key.match(/^no-(.+)$/);
|
||||
if (arguments.length === 2) {
|
||||
return (data[key] = value);
|
||||
} else if (no) {
|
||||
return data[no[1]] === false;
|
||||
} else {
|
||||
return data[key];
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize option data.
|
||||
option.init = function(obj) {
|
||||
return (data = obj || {});
|
||||
};
|
||||
|
||||
// List of options as flags.
|
||||
option.flags = function() {
|
||||
return Object.keys(data).filter(function(key) {
|
||||
// Don't display empty arrays.
|
||||
return !(Array.isArray(data[key]) && data[key].length === 0);
|
||||
}).map(function(key) {
|
||||
var val = data[key];
|
||||
return '--' + (val === false ? 'no-' : '') + key +
|
||||
(typeof val === 'boolean' ? '' : '=' + val);
|
||||
});
|
||||
};
|
||||
+445
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// Nodejs libs.
|
||||
var path = require('path');
|
||||
|
||||
// Extend generic "task" util lib.
|
||||
var parent = grunt.util.task.create();
|
||||
|
||||
// The module to be exported.
|
||||
var task = module.exports = Object.create(parent);
|
||||
|
||||
// A temporary registry of tasks and metadata.
|
||||
var registry = {tasks: [], untasks: [], meta: {}};
|
||||
|
||||
// The last specified tasks message.
|
||||
var lastInfo;
|
||||
|
||||
// Number of levels of recursion when loading tasks in collections.
|
||||
var loadTaskDepth = 0;
|
||||
|
||||
// Keep track of the number of log.error() calls.
|
||||
var errorcount;
|
||||
|
||||
// Override built-in registerTask.
|
||||
task.registerTask = function(name) {
|
||||
// Add task to registry.
|
||||
registry.tasks.push(name);
|
||||
// Register task.
|
||||
parent.registerTask.apply(task, arguments);
|
||||
// This task, now that it's been registered.
|
||||
var thisTask = task._tasks[name];
|
||||
// Metadata about the current task.
|
||||
thisTask.meta = grunt.util._.clone(registry.meta);
|
||||
// Override task function.
|
||||
var _fn = thisTask.fn;
|
||||
thisTask.fn = function(arg) {
|
||||
// Initialize the errorcount for this task.
|
||||
errorcount = grunt.fail.errorcount;
|
||||
// Return the number of errors logged during this task.
|
||||
Object.defineProperty(this, 'errorCount', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return grunt.fail.errorcount - errorcount;
|
||||
}
|
||||
});
|
||||
// Expose task.requires on `this`.
|
||||
this.requires = task.requires.bind(task);
|
||||
// Expose config.requires on `this`.
|
||||
this.requiresConfig = grunt.config.requires;
|
||||
// Return an options object with the specified defaults overriden by task-
|
||||
// specific overrides, via the "options" property.
|
||||
this.options = function() {
|
||||
var args = [{}].concat(grunt.util.toArray(arguments)).concat([
|
||||
grunt.config([name, 'options'])
|
||||
]);
|
||||
return grunt.util._.extend.apply(null, args);
|
||||
};
|
||||
// If this task was an alias or a multi task called without a target,
|
||||
// only log if in verbose mode.
|
||||
var logger = _fn.alias || (thisTask.multi && (!arg || arg === '*')) ? 'verbose' : 'log';
|
||||
// Actually log.
|
||||
grunt[logger].header('Running "' + this.nameArgs + '"' +
|
||||
(this.name !== this.nameArgs ? ' (' + this.name + ')' : '') + ' task');
|
||||
// If --debug was specified, log the path to this task's source file.
|
||||
grunt[logger].debug('Task source: ' + thisTask.meta.filepath);
|
||||
// Actually run the task.
|
||||
return _fn.apply(this, arguments);
|
||||
};
|
||||
return task;
|
||||
};
|
||||
|
||||
// Multi task targets can't start with _ or be a reserved property (options).
|
||||
function isValidMultiTaskTarget(target) {
|
||||
return !/^_|^options$/.test(target);
|
||||
}
|
||||
|
||||
// Normalize multi task files.
|
||||
task.normalizeMultiTaskFiles = function(data, target) {
|
||||
var prop, obj;
|
||||
var files = [];
|
||||
if (grunt.util.kindOf(data) === 'object') {
|
||||
if ('src' in data || 'dest' in data) {
|
||||
obj = {};
|
||||
for (prop in data) {
|
||||
if (prop !== 'options') {
|
||||
obj[prop] = data[prop];
|
||||
}
|
||||
}
|
||||
files.push(obj);
|
||||
} else if (grunt.util.kindOf(data.files) === 'object') {
|
||||
for (prop in data.files) {
|
||||
files.push({src: data.files[prop], dest: grunt.config.process(prop)});
|
||||
}
|
||||
} else if (Array.isArray(data.files)) {
|
||||
data.files.forEach(function(obj) {
|
||||
var prop;
|
||||
if ('src' in obj || 'dest' in obj) {
|
||||
files.push(obj);
|
||||
} else {
|
||||
for (prop in obj) {
|
||||
files.push({src: obj[prop], dest: grunt.config.process(prop)});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
files.push({src: data, dest: grunt.config.process(target)});
|
||||
}
|
||||
|
||||
// If no src/dest or files were specified, return an empty files array.
|
||||
if (files.length === 0) {
|
||||
grunt.verbose.writeln('File: ' + '[no files]'.yellow);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Process all normalized file objects.
|
||||
files = grunt.util._(files).chain().forEach(function(obj) {
|
||||
if (!('src' in obj) || !obj.src) { return; }
|
||||
// Normalize .src properties to flattened array.
|
||||
if (Array.isArray(obj.src)) {
|
||||
obj.src = grunt.util._.flatten(obj.src);
|
||||
} else {
|
||||
obj.src = [obj.src];
|
||||
}
|
||||
}).map(function(obj) {
|
||||
// Build options object, removing unwanted properties.
|
||||
var expandOptions = grunt.util._.extend({}, obj);
|
||||
delete expandOptions.src;
|
||||
delete expandOptions.dest;
|
||||
|
||||
// Expand file mappings.
|
||||
if (obj.expand) {
|
||||
return grunt.file.expandMapping(obj.src, obj.dest, expandOptions).map(function(mapObj) {
|
||||
// Copy obj properties to result.
|
||||
var result = grunt.util._.extend({}, obj);
|
||||
// Make a clone of the orig obj available.
|
||||
result.orig = grunt.util._.extend({}, obj);
|
||||
// Set .src and .dest, processing both as templates.
|
||||
result.src = grunt.config.process(mapObj.src);
|
||||
result.dest = grunt.config.process(mapObj.dest);
|
||||
// Remove unwanted properties.
|
||||
['expand', 'cwd', 'flatten', 'rename', 'ext'].forEach(function(prop) {
|
||||
delete result[prop];
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Copy obj properties to result, adding an .orig property.
|
||||
var result = grunt.util._.extend({}, obj);
|
||||
// Make a clone of the orig obj available.
|
||||
result.orig = grunt.util._.extend({}, obj);
|
||||
|
||||
if ('src' in result) {
|
||||
// Expose an expand-on-demand getter method as .src.
|
||||
Object.defineProperty(result, 'src', {
|
||||
enumerable: true,
|
||||
get: function fn() {
|
||||
var src;
|
||||
if (!('result' in fn)) {
|
||||
src = obj.src;
|
||||
// If src is an array, flatten it. Otherwise, make it into an array.
|
||||
src = Array.isArray(src) ? grunt.util._.flatten(src) : [src];
|
||||
// Expand src files, memoizing result.
|
||||
fn.result = grunt.file.expand(expandOptions, src);
|
||||
}
|
||||
return fn.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ('dest' in result) {
|
||||
result.dest = obj.dest;
|
||||
}
|
||||
|
||||
return result;
|
||||
}).flatten().value();
|
||||
|
||||
// Log this.file src and dest properties when --verbose is specified.
|
||||
if (grunt.option('verbose')) {
|
||||
files.forEach(function(obj) {
|
||||
var output = [];
|
||||
if ('src' in obj) {
|
||||
output.push(obj.src.length > 0 ? grunt.log.wordlist(obj.src) : '[no src]'.yellow);
|
||||
}
|
||||
if ('dest' in obj) {
|
||||
output.push('-> ' + (obj.dest ? String(obj.dest).cyan : '[no dest]'.yellow));
|
||||
}
|
||||
if (output.length > 0) {
|
||||
grunt.verbose.writeln('Files: ' + output.join(' '));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
// This is the most common "multi task" pattern.
|
||||
task.registerMultiTask = function(name, info, fn) {
|
||||
// If optional "info" string is omitted, shuffle arguments a bit.
|
||||
if (fn == null) {
|
||||
fn = info;
|
||||
info = 'Custom multi task.';
|
||||
}
|
||||
// Store a reference to the task object, in case the task gets renamed.
|
||||
var thisTask;
|
||||
task.registerTask(name, info, function(target) {
|
||||
// Guaranteed to always be the actual task name.
|
||||
var name = thisTask.name;
|
||||
// Arguments (sans target) as an array.
|
||||
this.args = grunt.util.toArray(arguments).slice(1);
|
||||
// If a target wasn't specified, run this task once for each target.
|
||||
if (!target || target === '*') {
|
||||
return task.runAllTargets(name, this.args);
|
||||
} else if (!isValidMultiTaskTarget(target)) {
|
||||
throw new Error('Invalid target "' + target + '" specified.');
|
||||
}
|
||||
// Fail if any required config properties have been omitted.
|
||||
this.requiresConfig([name, target]);
|
||||
// Return an options object with the specified defaults overriden by task-
|
||||
// and/or target-specific overrides, via the "options" property.
|
||||
this.options = function() {
|
||||
var targetObj = grunt.config([name, target]);
|
||||
var args = [{}].concat(grunt.util.toArray(arguments)).concat([
|
||||
grunt.config([name, 'options']),
|
||||
grunt.util.kindOf(targetObj) === 'object' ? targetObj.options : {}
|
||||
]);
|
||||
return grunt.util._.extend.apply(null, args);
|
||||
};
|
||||
// Expose data on `this` (as well as task.current).
|
||||
this.data = grunt.config([name, target]);
|
||||
// Expose normalized files object.
|
||||
this.files = task.normalizeMultiTaskFiles(this.data, target);
|
||||
// Expose normalized, flattened, uniqued array of src files.
|
||||
Object.defineProperty(this, 'filesSrc', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return grunt.util._(this.files).chain().pluck('src').flatten().uniq().value();
|
||||
}.bind(this)
|
||||
});
|
||||
// Expose the current target.
|
||||
this.target = target;
|
||||
// Recreate flags object so that the target isn't set as a flag.
|
||||
this.flags = {};
|
||||
this.args.forEach(function(arg) { this.flags[arg] = true; }, this);
|
||||
// Call original task function, passing in the target and any other args.
|
||||
return fn.apply(this, this.args);
|
||||
});
|
||||
|
||||
thisTask = task._tasks[name];
|
||||
thisTask.multi = true;
|
||||
};
|
||||
|
||||
// Init tasks don't require properties in config, and as such will preempt
|
||||
// config loading errors.
|
||||
task.registerInitTask = function(name, info, fn) {
|
||||
task.registerTask(name, info, fn);
|
||||
task._tasks[name].init = true;
|
||||
};
|
||||
|
||||
// Override built-in renameTask to use the registry.
|
||||
task.renameTask = function(oldname, newname) {
|
||||
// Add and remove task.
|
||||
registry.untasks.push(oldname);
|
||||
registry.tasks.push(newname);
|
||||
// Actually rename task.
|
||||
return parent.renameTask.apply(task, arguments);
|
||||
};
|
||||
|
||||
// If a property wasn't passed, run all task targets in turn.
|
||||
task.runAllTargets = function(taskname, args) {
|
||||
// Get an array of sub-property keys under the given config object.
|
||||
var targets = Object.keys(grunt.config.getRaw(taskname) || {});
|
||||
// Fail if there are no actual properties to iterate over.
|
||||
if (targets.length === 0) {
|
||||
grunt.log.error('No "' + taskname + '" targets found.');
|
||||
return false;
|
||||
}
|
||||
// Iterate over all valid target properties, running a task for each.
|
||||
targets.filter(isValidMultiTaskTarget).forEach(function(target) {
|
||||
// Be sure to pass in any additionally specified args.
|
||||
task.run([taskname, target].concat(args || []).join(':'));
|
||||
});
|
||||
};
|
||||
|
||||
// Load tasks and handlers from a given tasks file.
|
||||
var loadTaskStack = [];
|
||||
function loadTask(filepath) {
|
||||
// In case this was called recursively, save registry for later.
|
||||
loadTaskStack.push(registry);
|
||||
// Reset registry.
|
||||
registry = {tasks: [], untasks: [], meta: {info: lastInfo, filepath: filepath}};
|
||||
var filename = path.basename(filepath);
|
||||
var msg = 'Loading "' + filename + '" tasks...';
|
||||
var regCount = 0;
|
||||
var fn;
|
||||
try {
|
||||
// Load taskfile.
|
||||
fn = require(path.resolve(filepath));
|
||||
if (typeof fn === 'function') {
|
||||
fn.call(grunt, grunt);
|
||||
}
|
||||
grunt.verbose.write(msg).ok();
|
||||
// Log registered/renamed/unregistered tasks.
|
||||
['un', ''].forEach(function(prefix) {
|
||||
var list = grunt.util._.chain(registry[prefix + 'tasks']).uniq().sort().value();
|
||||
if (list.length > 0) {
|
||||
regCount++;
|
||||
grunt.verbose.writeln((prefix ? '- ' : '+ ') + grunt.log.wordlist(list));
|
||||
}
|
||||
});
|
||||
if (regCount === 0) {
|
||||
grunt.verbose.error('No tasks were registered or unregistered.');
|
||||
}
|
||||
} catch(e) {
|
||||
// Something went wrong.
|
||||
grunt.log.write(msg).error().verbose.error(e.stack).or.error(e);
|
||||
}
|
||||
// Restore registry.
|
||||
registry = loadTaskStack.pop() || {};
|
||||
}
|
||||
|
||||
// Log a message when loading tasks.
|
||||
function loadTasksMessage(info) {
|
||||
// Only keep track of names of top-level loaded tasks and collections,
|
||||
// not sub-tasks.
|
||||
if (loadTaskDepth === 0) { lastInfo = info; }
|
||||
grunt.verbose.subhead('Registering ' + info + ' tasks.');
|
||||
}
|
||||
|
||||
// Load tasks and handlers from a given directory.
|
||||
function loadTasks(tasksdir) {
|
||||
try {
|
||||
var files = grunt.file.glob.sync('*.{js,coffee}', {cwd: tasksdir, maxDepth: 1});
|
||||
// Load tasks from files.
|
||||
files.forEach(function(filename) {
|
||||
loadTask(path.join(tasksdir, filename));
|
||||
});
|
||||
} catch(e) {
|
||||
grunt.log.verbose.error(e.stack).or.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load tasks and handlers from a given directory.
|
||||
task.loadTasks = function(tasksdir) {
|
||||
loadTasksMessage('"' + tasksdir + '"');
|
||||
if (grunt.file.exists(tasksdir)) {
|
||||
loadTasks(tasksdir);
|
||||
} else {
|
||||
grunt.log.error('Tasks directory "' + tasksdir + '" not found.');
|
||||
}
|
||||
};
|
||||
|
||||
// Load tasks and handlers from a given locally-installed Npm module (installed
|
||||
// relative to the base dir).
|
||||
task.loadNpmTasks = function(name) {
|
||||
loadTasksMessage('"' + name + '" local Npm module');
|
||||
var root = path.resolve('node_modules');
|
||||
var pkgfile = path.join(root, name, 'package.json');
|
||||
var pkg = grunt.file.exists(pkgfile) ? grunt.file.readJSON(pkgfile) : {keywords: []};
|
||||
|
||||
// Process collection plugins.
|
||||
if (pkg.keywords && pkg.keywords.indexOf('gruntcollection') !== -1) {
|
||||
loadTaskDepth++;
|
||||
Object.keys(pkg.dependencies).forEach(function(depName) {
|
||||
// Npm sometimes pulls dependencies out if they're shared, so find
|
||||
// upwards if not found locally.
|
||||
var filepath = grunt.file.findup('node_modules/' + depName, {
|
||||
cwd: path.resolve('node_modules', name),
|
||||
nocase: true
|
||||
});
|
||||
if (filepath) {
|
||||
// Load this task plugin recursively.
|
||||
task.loadNpmTasks(path.relative(root, filepath));
|
||||
}
|
||||
});
|
||||
loadTaskDepth--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Process task plugins.
|
||||
var tasksdir = path.join(root, name, 'tasks');
|
||||
if (grunt.file.exists(tasksdir)) {
|
||||
loadTasks(tasksdir);
|
||||
} else {
|
||||
grunt.log.error('Local Npm module "' + name + '" not found. Is it installed?');
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize tasks.
|
||||
task.init = function(tasks, options) {
|
||||
if (!options) { options = {}; }
|
||||
|
||||
// Were only init tasks specified?
|
||||
var allInit = tasks.length > 0 && tasks.every(function(name) {
|
||||
var obj = task._taskPlusArgs(name).task;
|
||||
return obj && obj.init;
|
||||
});
|
||||
|
||||
// Get any local Gruntfile or tasks that might exist. Use --gruntfile override
|
||||
// if specified, otherwise search the current directory or any parent.
|
||||
var gruntfile = allInit ? null : grunt.option('gruntfile') ||
|
||||
grunt.file.findup('Gruntfile.{js,coffee}', {nocase: true});
|
||||
|
||||
var msg = 'Reading "' + (gruntfile ? path.basename(gruntfile) : '???') + '" Gruntfile...';
|
||||
if (gruntfile && grunt.file.exists(gruntfile)) {
|
||||
grunt.verbose.writeln().write(msg).ok();
|
||||
// Change working directory so that all paths are relative to the
|
||||
// Gruntfile's location (or the --base option, if specified).
|
||||
process.chdir(grunt.option('base') || path.dirname(gruntfile));
|
||||
// Load local tasks, if the file exists.
|
||||
loadTasksMessage('Gruntfile');
|
||||
loadTask(gruntfile);
|
||||
} else if (options.help || allInit) {
|
||||
// Don't complain about missing Gruntfile.
|
||||
} else if (grunt.option('gruntfile')) {
|
||||
// If --config override was specified and it doesn't exist, complain.
|
||||
grunt.log.writeln().write(msg).error();
|
||||
grunt.fatal('Unable to find "' + gruntfile + '" Gruntfile.', grunt.fail.code.MISSING_GRUNTFILE);
|
||||
} else if (!grunt.option('help')) {
|
||||
grunt.verbose.writeln().write(msg).error();
|
||||
grunt.log.writelns(
|
||||
'A valid Gruntfile could not be found. Please see the getting ' +
|
||||
'started guide for more information on how to configure grunt: ' +
|
||||
'http://gruntjs.com/getting-started'
|
||||
);
|
||||
grunt.fatal('Unable to find Gruntfile.', grunt.fail.code.MISSING_GRUNTFILE);
|
||||
}
|
||||
|
||||
// Load all user-specified --npm tasks.
|
||||
(grunt.option('npm') || []).forEach(task.loadNpmTasks);
|
||||
// Load all user-specified --tasks.
|
||||
(grunt.option('tasks') || []).forEach(task.loadTasks);
|
||||
};
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var grunt = require('../grunt');
|
||||
|
||||
// The module to be exported.
|
||||
var template = module.exports = {};
|
||||
|
||||
// External libs.
|
||||
template.date = require('dateformat');
|
||||
|
||||
// Format today's date.
|
||||
template.today = function(format) {
|
||||
return template.date(new Date(), format);
|
||||
};
|
||||
|
||||
// Template delimiters.
|
||||
var allDelimiters = {};
|
||||
|
||||
// Initialize template delimiters.
|
||||
template.addDelimiters = function(name, opener, closer) {
|
||||
var delimiters = allDelimiters[name] = {};
|
||||
// Used by grunt.
|
||||
delimiters.opener = opener;
|
||||
delimiters.closer = closer;
|
||||
// Generate RegExp patterns dynamically.
|
||||
var a = delimiters.opener.replace(/(.)/g, '\\$1');
|
||||
var b = '([\\s\\S]+?)' + delimiters.closer.replace(/(.)/g, '\\$1');
|
||||
// Used by Lo-Dash.
|
||||
delimiters.lodash = {
|
||||
evaluate: new RegExp(a + b, 'g'),
|
||||
interpolate: new RegExp(a + '=' + b, 'g'),
|
||||
escape: new RegExp(a + '-' + b, 'g')
|
||||
};
|
||||
};
|
||||
|
||||
// The underscore default template syntax should be a pretty sane default for
|
||||
// the config system.
|
||||
template.addDelimiters('config', '<%', '%>');
|
||||
|
||||
// Set Lo-Dash template delimiters.
|
||||
template.setDelimiters = function(name) {
|
||||
// Get the appropriate delimiters.
|
||||
var delimiters = allDelimiters[name in allDelimiters ? name : 'config'];
|
||||
// Tell Lo-Dash which delimiters to use.
|
||||
grunt.util._.templateSettings = delimiters.lodash;
|
||||
// Return the delimiters.
|
||||
return delimiters;
|
||||
};
|
||||
|
||||
// Process template + data with Lo-Dash.
|
||||
template.process = function(tmpl, options) {
|
||||
if (!options) { options = {}; }
|
||||
// Set delimiters, and get a opening match character.
|
||||
var delimiters = template.setDelimiters(options.delimiters);
|
||||
// Clone data, initializing to config data or empty object if omitted.
|
||||
var data = Object.create(options.data || grunt.config.data || {});
|
||||
// Expose grunt so that grunt utilities can be accessed, but only if it
|
||||
// doesn't conflict with an existing .grunt property.
|
||||
if (!('grunt' in data)) { data.grunt = grunt; }
|
||||
// Keep track of last change.
|
||||
var last = tmpl;
|
||||
try {
|
||||
// As long as tmpl contains template tags, render it and get the result,
|
||||
// otherwise just use the template string.
|
||||
while (tmpl.indexOf(delimiters.opener) >= 0) {
|
||||
tmpl = grunt.util._.template(tmpl, data);
|
||||
// Abort if template didn't change - nothing left to process!
|
||||
if (tmpl === last) { break; }
|
||||
last = tmpl;
|
||||
}
|
||||
} catch (e) {
|
||||
// In upgrading to Lo-Dash (or Underscore.js 1.3.3), \n and \r in template
|
||||
// tags now causes an exception to be thrown. Warn the user why this is
|
||||
// happening. https://github.com/documentcloud/underscore/issues/553
|
||||
if (String(e) === 'SyntaxError: Unexpected token ILLEGAL' && /\n|\r/.test(tmpl)) {
|
||||
grunt.log.errorlns('A special character was detected in this template. ' +
|
||||
'Inside template tags, the \\n and \\r special characters must be ' +
|
||||
'escaped as \\\\n and \\\\r. (grunt 0.4.0+)');
|
||||
}
|
||||
// Slightly better error message.
|
||||
e.message = 'An error occurred while processing a template (' + e.message + ').';
|
||||
grunt.warn(e, grunt.fail.code.TEMPLATE_ERROR);
|
||||
}
|
||||
// Normalize linefeeds and return.
|
||||
return grunt.util.normalizelf(tmpl);
|
||||
};
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Nodejs libs.
|
||||
var spawn = require('child_process').spawn;
|
||||
var nodeUtil = require('util');
|
||||
var path = require('path');
|
||||
|
||||
// The module to be exported.
|
||||
var util = module.exports = {};
|
||||
|
||||
// A few internal utilites, exposed.
|
||||
util.task = require('../util/task');
|
||||
util.namespace = require('../util/namespace');
|
||||
// Use instead of process.exit to ensure stdout/stderr are flushed
|
||||
// before exiting in Windows (Tested in Node.js v0.8.7)
|
||||
util.exit = require('../util/exit').exit;
|
||||
|
||||
// External libs.
|
||||
util.hooker = require('hooker');
|
||||
util.async = require('async');
|
||||
var _ = util._ = require('lodash');
|
||||
var which = require('which').sync;
|
||||
|
||||
// Mixin Underscore.string methods.
|
||||
_.str = require('underscore.string');
|
||||
_.mixin(_.str.exports());
|
||||
|
||||
// Return a function that normalizes the given function either returning a
|
||||
// value or accepting a "done" callback that accepts a single value.
|
||||
util.callbackify = function(fn) {
|
||||
return function callbackable() {
|
||||
// Invoke original function, getting its result.
|
||||
var result = fn.apply(this, arguments);
|
||||
// If the same number or less arguments were specified than fn accepts,
|
||||
// assume the "done" callback was already handled.
|
||||
var length = arguments.length;
|
||||
if (length === fn.length) { return; }
|
||||
// Otherwise, if the last argument is a function, assume it is a "done"
|
||||
// callback and call it.
|
||||
var done = arguments[length - 1];
|
||||
if (typeof done === 'function') { done(result); }
|
||||
};
|
||||
};
|
||||
|
||||
// Create a new Error object, with an origError property that will be dumped
|
||||
// if grunt was run with the --debug=9 option.
|
||||
util.error = function(err, origError) {
|
||||
if (!nodeUtil.isError(err)) { err = new Error(err); }
|
||||
if (origError) { err.origError = origError; }
|
||||
return err;
|
||||
};
|
||||
|
||||
// The line feed char for the current system.
|
||||
util.linefeed = process.platform === 'win32' ? '\r\n' : '\n';
|
||||
|
||||
// Normalize linefeeds in a string.
|
||||
util.normalizelf = function(str) {
|
||||
return str.replace(/\r\n|\n/g, util.linefeed);
|
||||
};
|
||||
|
||||
// What "kind" is a value?
|
||||
// I really need to rework https://github.com/cowboy/javascript-getclass
|
||||
var kindsOf = {};
|
||||
'Number String Boolean Function RegExp Array Date Error'.split(' ').forEach(function(k) {
|
||||
kindsOf['[object ' + k + ']'] = k.toLowerCase();
|
||||
});
|
||||
util.kindOf = function(value) {
|
||||
// Null or undefined.
|
||||
if (value == null) { return String(value); }
|
||||
// Everything else.
|
||||
return kindsOf[kindsOf.toString.call(value)] || 'object';
|
||||
};
|
||||
|
||||
// Coerce something to an Array.
|
||||
util.toArray = Function.call.bind(Array.prototype.slice);
|
||||
|
||||
// Return the string `str` repeated `n` times.
|
||||
util.repeat = function(n, str) {
|
||||
return new Array(n + 1).join(str || ' ');
|
||||
};
|
||||
|
||||
// Given str of "a/b", If n is 1, return "a" otherwise "b".
|
||||
util.pluralize = function(n, str, separator) {
|
||||
var parts = str.split(separator || '/');
|
||||
return n === 1 ? (parts[0] || '') : (parts[1] || '');
|
||||
};
|
||||
|
||||
// Recurse through objects and arrays, executing fn for each non-object.
|
||||
util.recurse = function recurse(value, fn, fnContinue) {
|
||||
var obj;
|
||||
if (fnContinue && fnContinue(value) === false) {
|
||||
// Skip value if necessary.
|
||||
return value;
|
||||
} else if (util.kindOf(value) === 'array') {
|
||||
// If value is an array, recurse.
|
||||
return value.map(function(value) {
|
||||
return recurse(value, fn, fnContinue);
|
||||
});
|
||||
} else if (util.kindOf(value) === 'object') {
|
||||
// If value is an object, recurse.
|
||||
obj = {};
|
||||
Object.keys(value).forEach(function(key) {
|
||||
obj[key] = recurse(value[key], fn, fnContinue);
|
||||
});
|
||||
return obj;
|
||||
} else {
|
||||
// Otherwise pass value into fn and return.
|
||||
return fn(value);
|
||||
}
|
||||
};
|
||||
|
||||
// Spawn a child process, capturing its stdout and stderr.
|
||||
util.spawn = function(opts, done) {
|
||||
// Build a result object and pass it (among other things) into the
|
||||
// done function.
|
||||
var callDone = function(code, stdout, stderr) {
|
||||
// Remove trailing whitespace (newline)
|
||||
stdout = _.rtrim(stdout);
|
||||
stderr = _.rtrim(stderr);
|
||||
// Create the result object.
|
||||
var result = {
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
code: code,
|
||||
toString: function() {
|
||||
if (code === 0) {
|
||||
return stdout;
|
||||
} else if ('fallback' in opts) {
|
||||
return opts.fallback;
|
||||
} else if (opts.grunt) {
|
||||
// grunt.log.error uses standard out, to be fixed in 0.5.
|
||||
return stderr || stdout;
|
||||
}
|
||||
return stderr;
|
||||
}
|
||||
};
|
||||
// On error (and no fallback) pass an error object, otherwise pass null.
|
||||
done(code === 0 || 'fallback' in opts ? null : new Error(stderr), result, code);
|
||||
};
|
||||
|
||||
var cmd, args;
|
||||
var pathSeparatorRe = /[\\\/]/g;
|
||||
if (opts.grunt) {
|
||||
cmd = process.argv[0];
|
||||
args = [process.argv[1]].concat(opts.args);
|
||||
} else {
|
||||
// On Windows, child_process.spawn will only file .exe files in the PATH,
|
||||
// not other executable types (grunt issue #155).
|
||||
try {
|
||||
if (!pathSeparatorRe.test(opts.cmd)) {
|
||||
// Only use which if cmd has no path component.
|
||||
cmd = which(opts.cmd);
|
||||
} else {
|
||||
cmd = opts.cmd.replace(pathSeparatorRe, path.sep);
|
||||
}
|
||||
} catch (err) {
|
||||
callDone(127, '', String(err));
|
||||
return;
|
||||
}
|
||||
args = opts.args;
|
||||
}
|
||||
|
||||
var child = spawn(cmd, args, opts.opts);
|
||||
var stdout = new Buffer('');
|
||||
var stderr = new Buffer('');
|
||||
if (child.stdout) {
|
||||
child.stdout.on('data', function(buf) {
|
||||
stdout = Buffer.concat([stdout, new Buffer(buf)]);
|
||||
});
|
||||
}
|
||||
if (child.stderr) {
|
||||
child.stderr.on('data', function(buf) {
|
||||
stderr = Buffer.concat([stderr, new Buffer(buf)]);
|
||||
});
|
||||
}
|
||||
child.on('close', function(code) {
|
||||
callDone(code, stdout.toString(), stderr.toString());
|
||||
});
|
||||
return child;
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// This seems to be required in Windows (as of Node.js 0.8.7) to ensure that
|
||||
// stdout/stderr are flushed before the process exits.
|
||||
|
||||
// https://gist.github.com/3427148
|
||||
// https://gist.github.com/3427357
|
||||
|
||||
exports.exit = function exit(exitCode) {
|
||||
if (process.stdout._pendingWriteReqs || process.stderr._pendingWriteReqs) {
|
||||
process.nextTick(function() {
|
||||
exit(exitCode);
|
||||
});
|
||||
} else {
|
||||
process.exit(exitCode);
|
||||
}
|
||||
};
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
// Split strings on dot, but only if dot isn't preceded by a backslash. Since
|
||||
// JavaScript doesn't support lookbehinds, use a placeholder for "\.", split
|
||||
// on dot, then replace the placeholder character with a dot.
|
||||
function getParts(str) {
|
||||
return str.replace(/\\\./g, '\uffff').split('.').map(function(s) {
|
||||
return s.replace(/\uffff/g, '.');
|
||||
});
|
||||
}
|
||||
|
||||
// Get the value of a deeply-nested property exist in an object.
|
||||
exports.get = function(obj, parts, create) {
|
||||
if (typeof parts === 'string') {
|
||||
parts = getParts(parts);
|
||||
}
|
||||
|
||||
var part;
|
||||
while (typeof obj === 'object' && obj && parts.length) {
|
||||
part = parts.shift();
|
||||
if (!(part in obj) && create) {
|
||||
obj[part] = {};
|
||||
}
|
||||
obj = obj[part];
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Set a deeply-nested property in an object, creating intermediary objects
|
||||
// as we go.
|
||||
exports.set = function(obj, parts, value) {
|
||||
parts = getParts(parts);
|
||||
|
||||
var prop = parts.pop();
|
||||
obj = exports.get(obj, parts, true);
|
||||
if (obj && typeof obj === 'object') {
|
||||
return (obj[prop] = value);
|
||||
}
|
||||
};
|
||||
|
||||
// Does a deeply-nested property exist in an object?
|
||||
exports.exists = function(obj, parts) {
|
||||
parts = getParts(parts);
|
||||
|
||||
var prop = parts.pop();
|
||||
obj = exports.get(obj, parts);
|
||||
|
||||
return typeof obj === 'object' && obj && prop in obj;
|
||||
};
|
||||
|
||||
}(typeof exports === 'object' && exports || this));
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* grunt
|
||||
* http://gruntjs.com/
|
||||
*
|
||||
* Copyright (c) 2013 "Cowboy" Ben Alman
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
|
||||
'use strict';
|
||||
|
||||
// Construct-o-rama.
|
||||
function Task() {
|
||||
// Information about the currently-running task.
|
||||
this.current = {};
|
||||
// Tasks.
|
||||
this._tasks = {};
|
||||
// Task queue.
|
||||
this._queue = [];
|
||||
// Queue placeholder (for dealing with nested tasks).
|
||||
this._placeholder = {placeholder: true};
|
||||
// Queue marker (for clearing the queue programatically).
|
||||
this._marker = {marker: true};
|
||||
// Options.
|
||||
this._options = {};
|
||||
// Is the queue running?
|
||||
this._running = false;
|
||||
// Success status of completed tasks.
|
||||
this._success = {};
|
||||
}
|
||||
|
||||
// Expose the constructor function.
|
||||
exports.Task = Task;
|
||||
|
||||
// Create a new Task instance.
|
||||
exports.create = function() {
|
||||
return new Task();
|
||||
};
|
||||
|
||||
// If the task runner is running or an error handler is not defined, throw
|
||||
// an exception. Otherwise, call the error handler directly.
|
||||
Task.prototype._throwIfRunning = function(obj) {
|
||||
if (this._running || !this._options.error) {
|
||||
// Throw an exception that the task runner will catch.
|
||||
throw obj;
|
||||
} else {
|
||||
// Not inside the task runner. Call the error handler and abort.
|
||||
this._options.error.call({name: null}, obj);
|
||||
}
|
||||
};
|
||||
|
||||
// Register a new task.
|
||||
Task.prototype.registerTask = function(name, info, fn) {
|
||||
// If optional "info" string is omitted, shuffle arguments a bit.
|
||||
if (fn == null) {
|
||||
fn = info;
|
||||
info = null;
|
||||
}
|
||||
// String or array of strings was passed instead of fn.
|
||||
var tasks;
|
||||
if (typeof fn !== 'function') {
|
||||
// Array of task names.
|
||||
tasks = this.parseArgs([fn]);
|
||||
// This task function just runs the specified tasks.
|
||||
fn = this.run.bind(this, fn);
|
||||
fn.alias = true;
|
||||
// Generate an info string if one wasn't explicitly passed.
|
||||
if (!info) {
|
||||
info = 'Alias for "' + tasks.join('", "') + '" task' +
|
||||
(tasks.length === 1 ? '' : 's') + '.';
|
||||
}
|
||||
} else if (!info) {
|
||||
info = 'Custom task.';
|
||||
}
|
||||
// Add task into cache.
|
||||
this._tasks[name] = {name: name, info: info, fn: fn};
|
||||
// Make chainable!
|
||||
return this;
|
||||
};
|
||||
|
||||
// Is the specified task an alias?
|
||||
Task.prototype.isTaskAlias = function(name) {
|
||||
return !!this._tasks[name].fn.alias;
|
||||
};
|
||||
|
||||
// Rename a task. This might be useful if you want to override the default
|
||||
// behavior of a task, while retaining the old name. This is a billion times
|
||||
// easier to implement than some kind of in-task "super" functionality.
|
||||
Task.prototype.renameTask = function(oldname, newname) {
|
||||
// Rename task.
|
||||
this._tasks[newname] = this._tasks[oldname];
|
||||
// Update name property of task.
|
||||
this._tasks[newname].name = newname;
|
||||
// Remove old name.
|
||||
delete this._tasks[oldname];
|
||||
// Make chainable!
|
||||
return this;
|
||||
};
|
||||
|
||||
// Argument parsing helper. Supports these signatures:
|
||||
// fn('foo') // ['foo']
|
||||
// fn('foo', 'bar', 'baz') // ['foo', 'bar', 'baz']
|
||||
// fn(['foo', 'bar', 'baz']) // ['foo', 'bar', 'baz']
|
||||
Task.prototype.parseArgs = function(args) {
|
||||
// Return the first argument if it's an array, otherwise return an array
|
||||
// of all arguments.
|
||||
return Array.isArray(args[0]) ? args[0] : [].slice.call(args);
|
||||
};
|
||||
|
||||
// Split a colon-delimited string into an array, unescaping (but not
|
||||
// splitting on) any \: escaped colons.
|
||||
Task.prototype.splitArgs = function(str) {
|
||||
if (!str) { return []; }
|
||||
// Store placeholder for \\ followed by \:
|
||||
str = str.replace(/\\\\/g, '\uFFFF').replace(/\\:/g, '\uFFFE');
|
||||
// Split on :
|
||||
return str.split(':').map(function(s) {
|
||||
// Restore place-held : followed by \\
|
||||
return s.replace(/\uFFFE/g, ':').replace(/\uFFFF/g, '\\');
|
||||
});
|
||||
};
|
||||
|
||||
// Given a task name, determine which actual task will be called, and what
|
||||
// arguments will be passed into the task callback. "foo" -> task "foo", no
|
||||
// args. "foo:bar:baz" -> task "foo:bar:baz" with no args (if "foo:bar:baz"
|
||||
// task exists), otherwise task "foo:bar" with arg "baz" (if "foo:bar" task
|
||||
// exists), otherwise task "foo" with args "bar" and "baz".
|
||||
Task.prototype._taskPlusArgs = function(name) {
|
||||
// Get task name / argument parts.
|
||||
var parts = this.splitArgs(name);
|
||||
// Start from the end, not the beginning!
|
||||
var i = parts.length;
|
||||
var task;
|
||||
do {
|
||||
// Get a task.
|
||||
task = this._tasks[parts.slice(0, i).join(':')];
|
||||
// If the task doesn't exist, decrement `i`, and if `i` is greater than
|
||||
// 0, repeat.
|
||||
} while (!task && --i > 0);
|
||||
// Just the args.
|
||||
var args = parts.slice(i);
|
||||
// Maybe you want to use them as flags instead of as positional args?
|
||||
var flags = {};
|
||||
args.forEach(function(arg) { flags[arg] = true; });
|
||||
// The task to run and the args to run it with.
|
||||
return {task: task, nameArgs: name, args: args, flags: flags};
|
||||
};
|
||||
|
||||
// Append things to queue in the correct spot.
|
||||
Task.prototype._push = function(things) {
|
||||
// Get current placeholder index.
|
||||
var index = this._queue.indexOf(this._placeholder);
|
||||
if (index === -1) {
|
||||
// No placeholder, add task+args objects to end of queue.
|
||||
this._queue = this._queue.concat(things);
|
||||
} else {
|
||||
// Placeholder exists, add task+args objects just before placeholder.
|
||||
[].splice.apply(this._queue, [index, 0].concat(things));
|
||||
}
|
||||
};
|
||||
|
||||
// Enqueue a task.
|
||||
Task.prototype.run = function() {
|
||||
// Parse arguments into an array, returning an array of task+args objects.
|
||||
var things = this.parseArgs(arguments).map(this._taskPlusArgs, this);
|
||||
// Throw an exception if any tasks weren't found.
|
||||
var fails = things.filter(function(thing) { return !thing.task; });
|
||||
if (fails.length > 0) {
|
||||
this._throwIfRunning(new Error('Task "' + fails[0].nameArgs + '" not found.'));
|
||||
return this;
|
||||
}
|
||||
// Append things to queue in the correct spot.
|
||||
this._push(things);
|
||||
// Make chainable!
|
||||
return this;
|
||||
};
|
||||
|
||||
// Add a marker to the queue to facilitate clearing it programatically.
|
||||
Task.prototype.mark = function() {
|
||||
this._push(this._marker);
|
||||
// Make chainable!
|
||||
return this;
|
||||
};
|
||||
|
||||
// Run a task function, handling this.async / return value.
|
||||
Task.prototype.runTaskFn = function(context, fn, done) {
|
||||
// Async flag.
|
||||
var async = false;
|
||||
|
||||
// Update the internal status object and run the next task.
|
||||
var complete = function(success) {
|
||||
var err = null;
|
||||
if (success === false) {
|
||||
// Since false was passed, the task failed generically.
|
||||
err = new Error('Task "' + context.nameArgs + '" failed.');
|
||||
} else if (success instanceof Error || {}.toString.call(success) === '[object Error]') {
|
||||
// An error object was passed, so the task failed specifically.
|
||||
err = success;
|
||||
success = false;
|
||||
} else {
|
||||
// The task succeeded.
|
||||
success = true;
|
||||
}
|
||||
// The task has ended, reset the current task object.
|
||||
this.current = {};
|
||||
// A task has "failed" only if it returns false (async) or if the
|
||||
// function returned by .async is passed false.
|
||||
this._success[context.nameArgs] = success;
|
||||
// If task failed, call error handler.
|
||||
if (!success && this._options.error) {
|
||||
this._options.error.call({name: context.name, nameArgs: context.nameArgs}, err);
|
||||
}
|
||||
done(err, success);
|
||||
}.bind(this);
|
||||
|
||||
// When called, sets the async flag and returns a function that can
|
||||
// be used to continue processing the queue.
|
||||
context.async = function() {
|
||||
async = true;
|
||||
// The returned function should execute asynchronously in case
|
||||
// someone tries to do this.async()(); inside a task (WTF).
|
||||
return function(success) {
|
||||
setTimeout(function() { complete(success); }, 1);
|
||||
};
|
||||
};
|
||||
|
||||
// Expose some information about the currently-running task.
|
||||
this.current = context;
|
||||
|
||||
try {
|
||||
// Get the current task and run it, setting `this` inside the task
|
||||
// function to be something useful.
|
||||
var success = fn.call(context);
|
||||
// If the async flag wasn't set, process the next task in the queue.
|
||||
if (!async) {
|
||||
complete(success);
|
||||
}
|
||||
} catch (err) {
|
||||
complete(err);
|
||||
}
|
||||
};
|
||||
|
||||
// Begin task queue processing. Ie. run all tasks.
|
||||
Task.prototype.start = function() {
|
||||
// Abort if already running.
|
||||
if (this._running) { return false; }
|
||||
// Actually process the next task.
|
||||
var nextTask = function() {
|
||||
// Get next task+args object from queue.
|
||||
var thing;
|
||||
// Skip any placeholders or markers.
|
||||
do {
|
||||
thing = this._queue.shift();
|
||||
} while (thing === this._placeholder || thing === this._marker);
|
||||
// If queue was empty, we're all done.
|
||||
if (!thing) {
|
||||
this._running = false;
|
||||
if (this._options.done) {
|
||||
this._options.done();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Add a placeholder to the front of the queue.
|
||||
this._queue.unshift(this._placeholder);
|
||||
|
||||
// Expose some information about the currently-running task.
|
||||
var context = {
|
||||
// The current task name plus args, as-passed.
|
||||
nameArgs: thing.nameArgs,
|
||||
// The current task name.
|
||||
name: thing.task.name,
|
||||
// The current task arguments.
|
||||
args: thing.args,
|
||||
// The current arguments, available as named flags.
|
||||
flags: thing.flags
|
||||
};
|
||||
|
||||
// Actually run the task function (handling this.async, etc)
|
||||
this.runTaskFn(context, function() {
|
||||
return thing.task.fn.apply(this, this.args);
|
||||
}, nextTask);
|
||||
|
||||
}.bind(this);
|
||||
|
||||
// Update flag.
|
||||
this._running = true;
|
||||
// Process the next task.
|
||||
nextTask();
|
||||
};
|
||||
|
||||
// Clear remaining tasks from the queue.
|
||||
Task.prototype.clearQueue = function(options) {
|
||||
if (!options) { options = {}; }
|
||||
if (options.untilMarker) {
|
||||
this._queue.splice(0, this._queue.indexOf(this._marker) + 1);
|
||||
} else {
|
||||
this._queue = [];
|
||||
}
|
||||
// Make chainable!
|
||||
return this;
|
||||
};
|
||||
|
||||
// Test to see if all of the given tasks have succeeded.
|
||||
Task.prototype.requires = function() {
|
||||
this.parseArgs(arguments).forEach(function(name) {
|
||||
var success = this._success[name];
|
||||
if (!success) {
|
||||
throw new Error('Required task "' + name +
|
||||
'" ' + (success === false ? 'failed' : 'must be run first') + '.');
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// Override default options.
|
||||
Task.prototype.options = function(options) {
|
||||
Object.keys(options).forEach(function(name) {
|
||||
this._options[name] = options[name];
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
}(typeof exports === 'object' && exports || this));
|
||||
Reference in New Issue
Block a user