-
Notifications
You must be signed in to change notification settings - Fork 53
/
newer.js
208 lines (176 loc) · 5.94 KB
/
newer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
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();
}
});
};