Files
2025-07-24 17:21:45 +08:00

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();
}
});
};