Version 1
[yaffs-website] / node_modules / grunt / lib / grunt / task.js
diff --git a/node_modules/grunt/lib/grunt/task.js b/node_modules/grunt/lib/grunt/task.js
new file mode 100644 (file)
index 0000000..f31ff2e
--- /dev/null
@@ -0,0 +1,458 @@
+'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) {
+    // Guaranteed to always be the actual task name.
+    var name = thisTask.name;
+    // 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 overwritten by task-
+    // specific overrides, via the "options" property.
+    this.options = function() {
+      var args = [{}].concat(grunt.util.toArray(arguments)).concat([
+        grunt.config([name, 'options'])
+      ]);
+      var options = grunt.util._.extend.apply(null, args);
+      grunt.verbose.writeflags(options, 'Options');
+      return options;
+    };
+    // 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)) {
+      grunt.util._.flattenDeep(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 overwritten 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 : {}
+      ]);
+      var options = grunt.util._.extend.apply(null, args);
+      grunt.verbose.writeflags(options, 'Options');
+      return options;
+    };
+    // 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);
+    // 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().map('src').flatten().uniq().value();
+      }.bind(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) {
+  var result;
+  try {
+    // Actually rename task.
+    result = parent.renameTask.apply(task, arguments);
+    // Add and remove task.
+    registry.untasks.push(oldname);
+    registry.tasks.push(newname);
+    // Return result.
+    return result;
+  } catch (e) {
+    grunt.log.error(e.message);
+  }
+};
+
+// 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) || {});
+  // Remove invalid target properties.
+  targets = targets.filter(isValidMultiTaskTarget);
+  // 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 targets, running a task for each.
+  targets.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.warn('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, msg;
+  if (allInit || options.gruntfile === false) {
+    gruntfile = null;
+  } else {
+    gruntfile = grunt.option('gruntfile') ||
+      grunt.file.findup('Gruntfile.{js,coffee}', {nocase: true});
+    msg = 'Reading "' + (gruntfile ? path.basename(gruntfile) : '???') + '" Gruntfile...';
+  }
+
+  if (options.gruntfile === false) {
+    // Grunt was run as a lib with {gruntfile: false}.
+  } else 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') || []).map(String).forEach(task.loadNpmTasks);
+  // Load all user-specified --tasks.
+  (grunt.option('tasks') || []).map(String).forEach(task.loadTasks);
+};