209 lines
5.9 KiB
JavaScript
209 lines
5.9 KiB
JavaScript
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
var async = require('async');
|
|
var rimraf = require('rimraf');
|
|
|
|
var util = require('../lib/util');
|
|
|
|
var counter = 0;
|
|
var configCache = {};
|
|
|
|
function cacheConfig(config) {
|
|
++counter;
|
|
configCache[counter] = config;
|
|
return counter;
|
|
}
|
|
|
|
function pluckConfig(id) {
|
|
if (!configCache.hasOwnProperty(id)) {
|
|
throw new Error('Failed to find id in cache');
|
|
}
|
|
var config = configCache[id];
|
|
delete configCache[id];
|
|
return config;
|
|
}
|
|
|
|
function nullOverride(details, include) {
|
|
include(false);
|
|
}
|
|
|
|
function createTask(grunt) {
|
|
return function(taskName, targetName) {
|
|
var tasks = [];
|
|
var prefix = this.name;
|
|
if (!targetName) {
|
|
if (!grunt.config(taskName)) {
|
|
grunt.fatal('The "' + prefix + '" prefix is not supported for aliases');
|
|
return;
|
|
}
|
|
Object.keys(grunt.config(taskName)).forEach(function(targetName) {
|
|
if (!/^_|^options$/.test(targetName)) {
|
|
tasks.push(prefix + ':' + taskName + ':' + targetName);
|
|
}
|
|
});
|
|
return grunt.task.run(tasks);
|
|
}
|
|
var args = Array.prototype.slice.call(arguments, 2).join(':');
|
|
var options = this.options({
|
|
cache: path.join(__dirname, '..', '.cache'),
|
|
override: nullOverride,
|
|
tolerance: 0 // allowed difference between src and dst in ms
|
|
});
|
|
|
|
// support deprecated timestamps option
|
|
if (options.timestamps) {
|
|
grunt.log.warn('DEPRECATED OPTION. Use the "cache" option instead');
|
|
options.cache = options.timestamps;
|
|
}
|
|
|
|
// Sanity check for the tolerance option
|
|
if (typeof options.tolerance !== 'number') {
|
|
grunt.log.warn('The tolerance value must be a number, ignoring current ' +
|
|
'value');
|
|
options.tolerance = 0;
|
|
}
|
|
if (options.tolerance < 0) {
|
|
grunt.log.warn('A tolerance value of ' + options.tolerance +
|
|
' is invalid');
|
|
options.tolerance = 0;
|
|
}
|
|
|
|
var done = this.async();
|
|
|
|
var originalConfig = grunt.config.get([taskName, targetName]);
|
|
var config = grunt.util._.clone(originalConfig);
|
|
|
|
/**
|
|
* Special handling for tasks that expect the `files` config to be a string
|
|
* or array of string source paths.
|
|
*/
|
|
var srcFiles = true;
|
|
if (typeof config.files === 'string') {
|
|
config.src = [config.files];
|
|
delete config.files;
|
|
srcFiles = false;
|
|
} else if (Array.isArray(config.files) &&
|
|
typeof config.files[0] === 'string') {
|
|
config.src = config.files;
|
|
delete config.files;
|
|
srcFiles = false;
|
|
}
|
|
|
|
var stamp = util.getStampPath(options.cache, taskName, targetName);
|
|
var previous;
|
|
try {
|
|
previous = fs.statSync(stamp).mtime;
|
|
} catch (err) {
|
|
// task has never succeeded before
|
|
previous = new Date(0);
|
|
}
|
|
|
|
function override(filePath, time, include) {
|
|
var details = {
|
|
task: taskName,
|
|
target: targetName,
|
|
path: filePath,
|
|
time: time
|
|
};
|
|
options.override(details, include);
|
|
}
|
|
|
|
var files = grunt.task.normalizeMultiTaskFiles(config, targetName);
|
|
util.filterFilesByTime(
|
|
files, previous, options.tolerance, override, function(e, newerFiles) {
|
|
if (e) {
|
|
return done(e);
|
|
} else if (newerFiles.length === 0) {
|
|
grunt.log.writeln('No newer files to process.');
|
|
return done();
|
|
}
|
|
|
|
/**
|
|
* If we started out with only src files in the files config,
|
|
* transform the newerFiles array into an array of source files.
|
|
*/
|
|
if (!srcFiles) {
|
|
newerFiles = newerFiles.map(function(obj) {
|
|
return obj.src;
|
|
});
|
|
}
|
|
|
|
// configure target with only newer files
|
|
config.files = newerFiles;
|
|
delete config.src;
|
|
delete config.dest;
|
|
grunt.config.set([taskName, targetName], config);
|
|
|
|
// because we modified the task config, cache the original
|
|
var id = cacheConfig(originalConfig);
|
|
|
|
// run the task, and attend to postrun tasks
|
|
var qualified = taskName + ':' + targetName;
|
|
var tasks = [
|
|
qualified + (args ? ':' + args : ''),
|
|
'newer-postrun:' + qualified + ':' + id + ':' + options.cache
|
|
];
|
|
grunt.task.run(tasks);
|
|
|
|
done();
|
|
});
|
|
|
|
};
|
|
}
|
|
|
|
|
|
/** @param {Object} grunt Grunt. */
|
|
module.exports = function(grunt) {
|
|
|
|
grunt.registerTask(
|
|
'newer', 'Run a task with only those source files that have been ' +
|
|
'modified since the last successful run.', createTask(grunt));
|
|
|
|
var deprecated = 'DEPRECATED TASK. Use the "newer" task instead';
|
|
grunt.registerTask(
|
|
'any-newer', deprecated, function() {
|
|
grunt.log.warn(deprecated);
|
|
var args = Array.prototype.join.call(arguments, ':');
|
|
grunt.task.run(['newer:' + args]);
|
|
});
|
|
|
|
var internal = 'Internal task.';
|
|
grunt.registerTask(
|
|
'newer-postrun', internal, function(taskName, targetName, id, dir) {
|
|
|
|
// if dir includes a ':', grunt will split it among multiple args
|
|
dir = Array.prototype.slice.call(arguments, 3).join(':');
|
|
grunt.file.write(util.getStampPath(dir, taskName, targetName),
|
|
String(Date.now()));
|
|
|
|
// reconfigure task with original config
|
|
grunt.config.set([taskName, targetName], pluckConfig(id));
|
|
|
|
});
|
|
|
|
var clean = 'Remove cached timestamps.';
|
|
grunt.registerTask(
|
|
'newer-clean', clean, function(taskName, targetName) {
|
|
var done = this.async();
|
|
|
|
/**
|
|
* This intentionally only works with the default cache dir. If a
|
|
* custom cache dir is provided, it is up to the user to keep it clean.
|
|
*/
|
|
var cacheDir = path.join(__dirname, '..', '.cache');
|
|
if (taskName && targetName) {
|
|
cacheDir = util.getStampPath(cacheDir, taskName, targetName);
|
|
} else if (taskName) {
|
|
cacheDir = path.join(cacheDir, taskName);
|
|
}
|
|
if (grunt.file.exists(cacheDir)) {
|
|
grunt.log.writeln('Cleaning ' + cacheDir);
|
|
rimraf(cacheDir, done);
|
|
} else {
|
|
done();
|
|
}
|
|
});
|
|
|
|
};
|