1 | var fs = require('fs');
|
2 | var path = require('path');
|
3 |
|
4 | var async = require('async');
|
5 | var rimraf = require('rimraf');
|
6 |
|
7 | var util = require('../lib/util');
|
8 |
|
9 | var counter = 0;
|
10 | var configCache = {};
|
11 |
|
12 | function cacheConfig(config) {
|
13 | ++counter;
|
14 | configCache[counter] = config;
|
15 | return counter;
|
16 | }
|
17 |
|
18 | function pluckConfig(id) {
|
19 | if (!configCache.hasOwnProperty(id)) {
|
20 | throw new Error('Failed to find id in cache');
|
21 | }
|
22 | var config = configCache[id];
|
23 | delete configCache[id];
|
24 | return config;
|
25 | }
|
26 |
|
27 | function nullOverride(details, include) {
|
28 | include(false);
|
29 | }
|
30 |
|
31 | function createTask(grunt) {
|
32 | return function(taskName, targetName) {
|
33 | var tasks = [];
|
34 | var prefix = this.name;
|
35 | if (!targetName) {
|
36 | if (!grunt.config(taskName)) {
|
37 | grunt.fatal('The "' + prefix + '" prefix is not supported for aliases');
|
38 | return;
|
39 | }
|
40 | Object.keys(grunt.config(taskName)).forEach(function(targetName) {
|
41 | if (!/^_|^options$/.test(targetName)) {
|
42 | tasks.push(prefix + ':' + taskName + ':' + targetName);
|
43 | }
|
44 | });
|
45 | return grunt.task.run(tasks);
|
46 | }
|
47 | var args = Array.prototype.slice.call(arguments, 2).join(':');
|
48 | var options = this.options({
|
49 | cache: path.join(__dirname, '..', '.cache'),
|
50 | override: nullOverride
|
51 | });
|
52 |
|
53 |
|
54 | if (options.timestamps) {
|
55 | grunt.log.warn('DEPRECATED OPTION. Use the "cache" option instead');
|
56 | options.cache = options.timestamps;
|
57 | }
|
58 |
|
59 | var done = this.async();
|
60 |
|
61 | var originalConfig = grunt.config.get([taskName, targetName]);
|
62 | var config = grunt.util._.clone(originalConfig);
|
63 |
|
64 | |
65 |
|
66 |
|
67 |
|
68 | var srcFiles = true;
|
69 | if (typeof config.files === 'string') {
|
70 | config.src = [config.files];
|
71 | delete config.files;
|
72 | srcFiles = false;
|
73 | } else if (Array.isArray(config.files) &&
|
74 | typeof config.files[0] === 'string') {
|
75 | config.src = config.files;
|
76 | delete config.files;
|
77 | srcFiles = false;
|
78 | }
|
79 |
|
80 | var stamp = util.getStampPath(options.cache, taskName, targetName);
|
81 | var previous;
|
82 | try {
|
83 | previous = fs.statSync(stamp).mtime;
|
84 | } catch (err) {
|
85 |
|
86 | previous = new Date(0);
|
87 | }
|
88 |
|
89 | function override(filePath, time, include) {
|
90 | var details = {
|
91 | task: taskName,
|
92 | target: targetName,
|
93 | path: filePath,
|
94 | time: time
|
95 | };
|
96 | options.override(details, include);
|
97 | }
|
98 |
|
99 | var files = grunt.task.normalizeMultiTaskFiles(config, targetName);
|
100 | util.filterFilesByTime(files, previous, override, function(e, newerFiles) {
|
101 | if (e) {
|
102 | return done(e);
|
103 | } else if (newerFiles.length === 0) {
|
104 | grunt.log.writeln('No newer files to process.');
|
105 | return done();
|
106 | }
|
107 |
|
108 | |
109 |
|
110 |
|
111 |
|
112 | if (!srcFiles) {
|
113 | newerFiles = newerFiles.map(function(obj) {
|
114 | return obj.src;
|
115 | });
|
116 | }
|
117 |
|
118 |
|
119 | config.files = newerFiles;
|
120 | delete config.src;
|
121 | delete config.dest;
|
122 | grunt.config.set([taskName, targetName], config);
|
123 |
|
124 |
|
125 | var id = cacheConfig(originalConfig);
|
126 |
|
127 |
|
128 | var qualified = taskName + ':' + targetName;
|
129 | var tasks = [
|
130 | qualified + (args ? ':' + args : ''),
|
131 | 'newer-postrun:' + qualified + ':' + id + ':' + options.cache
|
132 | ];
|
133 | grunt.task.run(tasks);
|
134 |
|
135 | done();
|
136 | });
|
137 |
|
138 | };
|
139 | }
|
140 |
|
141 |
|
142 |
|
143 | module.exports = function(grunt) {
|
144 |
|
145 | grunt.registerTask(
|
146 | 'newer', 'Run a task with only those source files that have been ' +
|
147 | 'modified since the last successful run.', createTask(grunt));
|
148 |
|
149 | var deprecated = 'DEPRECATED TASK. Use the "newer" task instead';
|
150 | grunt.registerTask(
|
151 | 'any-newer', deprecated, function() {
|
152 | grunt.log.warn(deprecated);
|
153 | var args = Array.prototype.join.call(arguments, ':');
|
154 | grunt.task.run(['newer:' + args]);
|
155 | });
|
156 |
|
157 | var internal = 'Internal task.';
|
158 | grunt.registerTask(
|
159 | 'newer-postrun', internal, function(taskName, targetName, id, dir) {
|
160 |
|
161 |
|
162 | dir = Array.prototype.slice.call(arguments, 3).join(':');
|
163 | grunt.file.write(util.getStampPath(dir, taskName, targetName),
|
164 | String(Date.now()));
|
165 |
|
166 |
|
167 | grunt.config.set([taskName, targetName], pluckConfig(id));
|
168 |
|
169 | });
|
170 |
|
171 | var clean = 'Remove cached timestamps.';
|
172 | grunt.registerTask(
|
173 | 'newer-clean', clean, function(taskName, targetName) {
|
174 | var done = this.async();
|
175 |
|
176 | |
177 |
|
178 |
|
179 |
|
180 | var cacheDir = path.join(__dirname, '..', '.cache');
|
181 | if (taskName && targetName) {
|
182 | cacheDir = util.getStampPath(cacheDir, taskName, targetName);
|
183 | } else if (taskName) {
|
184 | cacheDir = path.join(cacheDir, taskName);
|
185 | }
|
186 | if (grunt.file.exists(cacheDir)) {
|
187 | grunt.log.writeln('Cleaning ' + cacheDir);
|
188 | rimraf(cacheDir, done);
|
189 | } else {
|
190 | done();
|
191 | }
|
192 | });
|
193 |
|
194 | };
|