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