grunt-newer
Advanced tools
Comparing version 0.6.1 to 0.7.0
@@ -12,2 +12,3 @@ var crypto = require('crypto'); | ||
* @param {Date} time The comparison time. | ||
* @param {function(string, Date, function(boolean))} override Override. | ||
* @param {function(Err, Array.<string>)} callback Callback called with any | ||
@@ -17,3 +18,3 @@ * error and a list of files that have mtimes newer than the provided time. | ||
var filterPathsByTime = exports.filterPathsByTime = function(paths, time, | ||
callback) { | ||
override, callback) { | ||
async.map(paths, fs.stat, function(err, stats) { | ||
@@ -23,5 +24,17 @@ if (err) { | ||
} | ||
callback(null, paths.filter(function(filePath, index) { | ||
return stats[index].mtime > time; | ||
})); | ||
var olderPaths = []; | ||
var newerPaths = paths.filter(function(filePath, index) { | ||
var newer = stats[index].mtime > time; | ||
if (!newer) { | ||
olderPaths.push(filePath); | ||
} | ||
return newer; | ||
}); | ||
async.filter(olderPaths, function(filePath, include) { | ||
override(filePath, time, include); | ||
}, function(overrides) { | ||
callback(null, newerPaths.concat(overrides)); | ||
}); | ||
}); | ||
@@ -35,2 +48,3 @@ }; | ||
* @param {Date} time The comparison time. | ||
* @param {function(string, Date, function(boolean))} override Override. | ||
* @param {function(Err, boolean)} callback Callback called with any error and | ||
@@ -40,3 +54,3 @@ * a boolean indicating whether any one of the supplied files is newer than | ||
*/ | ||
var anyNewer = exports.anyNewer = function(paths, time, callback) { | ||
var anyNewer = exports.anyNewer = function(paths, time, override, callback) { | ||
var complete = 0; | ||
@@ -50,8 +64,15 @@ function iterate() { | ||
return callback(null, true); | ||
} else { | ||
override(paths[complete], time, function(include) { | ||
if (include) { | ||
callback(null, true); | ||
} else { | ||
++complete; | ||
if (complete >= paths.length) { | ||
return callback(null, false); | ||
} | ||
iterate(); | ||
} | ||
}); | ||
} | ||
++complete; | ||
if (complete >= paths.length) { | ||
return callback(null, false); | ||
} | ||
iterate(); | ||
}); | ||
@@ -71,2 +92,3 @@ } | ||
* @param {Date} previous Comparison time. | ||
* @param {function(string, Date, function(boolean))} override Override. | ||
* @param {function(Error, Array.<Object>)} callback Callback called with | ||
@@ -77,3 +99,3 @@ * modified file config objects. Objects with no more src files are | ||
var filterFilesByTime = exports.filterFilesByTime = function(files, previous, | ||
callback) { | ||
override, callback) { | ||
async.map(files, function(obj, done) { | ||
@@ -88,10 +110,11 @@ if (obj.dest) { | ||
// when src and dest are same, compare to previous | ||
return filterPathsByTime(obj.src, previous, function(err, src) { | ||
if (err) { | ||
return done(err); | ||
} | ||
done(null, {src: src, dest: obj.dest}); | ||
}); | ||
return filterPathsByTime(obj.src, previous, override, | ||
function(err, src) { | ||
if (err) { | ||
return done(err); | ||
} | ||
done(null, {src: src, dest: obj.dest}); | ||
}); | ||
} | ||
return anyNewer(obj.src, stats.mtime, function(err, any) { | ||
return anyNewer(obj.src, stats.mtime, override, function(err, any) { | ||
done(err, any && obj); | ||
@@ -101,3 +124,3 @@ }); | ||
} else { | ||
filterPathsByTime(obj.src, previous, function(err, src) { | ||
filterPathsByTime(obj.src, previous, override, function(err, src) { | ||
if (err) { | ||
@@ -104,0 +127,0 @@ return done(err); |
{ | ||
"name": "grunt-newer", | ||
"description": "Run Grunt tasks with only those source files modified since the last successful run.", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"homepage": "https://github.com/tschaub/grunt-newer", | ||
@@ -33,9 +33,9 @@ "author": { | ||
"grunt": "0.4.2", | ||
"grunt-cli": "0.1.11", | ||
"grunt-cli": "0.1.13", | ||
"grunt-contrib-watch": "0.5.3", | ||
"grunt-contrib-jshint": "0.7.2", | ||
"chai": "1.8.1", | ||
"grunt-cafe-mocha": "0.1.10", | ||
"wrench": "1.5.4", | ||
"tmp": "0.0.21", | ||
"grunt-contrib-jshint": "0.8.0", | ||
"chai": "1.9.0", | ||
"grunt-cafe-mocha": "0.1.11", | ||
"wrench": "1.5.7", | ||
"tmp": "0.0.23", | ||
"grunt-contrib-clean": "0.5.0", | ||
@@ -54,5 +54,5 @@ "mock-fs": "2.x" | ||
"dependencies": { | ||
"async": "0.2.9", | ||
"rimraf": "2.2.4" | ||
"async": "0.2.10", | ||
"rimraf": "2.2.6" | ||
} | ||
} |
@@ -105,3 +105,3 @@ # grunt-newer | ||
In most cases, you shouldn't need to add any special configuration for the `newer` task. Just `grunt.loadNpmTasks('grunt-newer')` and you can use `newer` as a prefix to your other tasks. The single option below is available if you need a custom configuration. | ||
In most cases, you shouldn't need to add any special configuration for the `newer` task. Just `grunt.loadNpmTasks('grunt-newer')` and you can use `newer` as a prefix to your other tasks. The options below are available for advanced usage. | ||
@@ -126,2 +126,33 @@ #### <a id="optionscache">options.cache</a> | ||
#### <a id="optionsoverride">options.override</a> | ||
* type: `function(Object, function(boolean))` | ||
* default: `null` | ||
The `newer` task determines which files to include for a specific task based on file modification time. There are occassions where you may want to include a file even if it has not been modified. For example, if a LESS file imports some other files, you will want to include it if any of the imports have been modified. To support this, you can provide an `override` function that takes two arguments: | ||
* **details** - `Object` | ||
* **task** - `string` The currently running task name. | ||
* **target** - `string` The currently running target name. | ||
* **path** - `string` The path to a `src` file that appears to be "older" (not modified since the time below). | ||
* **time** - `Date` The comparison time. For tasks with `dest` files, this is the modification time of the `dest` file. For tasks without `dest` files, this is the last successful run time of the same task. | ||
* **include** - `function(boolean)` A callback that determines whether this `src` file should be included. Call with `true` to include or `false` to exclude the file. | ||
Example use of the `override` option: | ||
```js | ||
grunt.initConfig({ | ||
newer: { | ||
options: { | ||
override: function(detail, include) { | ||
if (detail.task === 'less') { | ||
checkForModifiedImports(detail.path, detail.time, include); | ||
} else { | ||
include(false); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
``` | ||
## That's it | ||
@@ -128,0 +159,0 @@ |
@@ -27,10 +27,14 @@ var fs = require('fs'); | ||
function nullOverride(details, include) { | ||
include(false); | ||
} | ||
function createTask(grunt) { | ||
return function(name, target) { | ||
return function(taskName, targetName) { | ||
var tasks = []; | ||
var prefix = this.name; | ||
if (!target) { | ||
Object.keys(grunt.config(name)).forEach(function(target) { | ||
if (!/^_|^options$/.test(target)) { | ||
tasks.push(prefix + ':' + name + ':' + target); | ||
if (!targetName) { | ||
Object.keys(grunt.config(taskName)).forEach(function(targetName) { | ||
if (!/^_|^options$/.test(targetName)) { | ||
tasks.push(prefix + ':' + taskName + ':' + targetName); | ||
} | ||
@@ -42,3 +46,4 @@ }); | ||
var options = this.options({ | ||
cache: path.join(__dirname, '..', '.cache') | ||
cache: path.join(__dirname, '..', '.cache'), | ||
override: nullOverride | ||
}); | ||
@@ -52,8 +57,10 @@ | ||
var originalConfig = grunt.config.get([name, target]); | ||
var done = this.async(); | ||
var originalConfig = grunt.config.get([taskName, targetName]); | ||
var config = grunt.util._.clone(originalConfig); | ||
/** | ||
* Special handling for watch task. This is a multitask that expects | ||
* the `files` config to be a string or array of string source paths. | ||
* Special handling for tasks that expect the `files` config to be a string | ||
* or array of string source paths. | ||
*/ | ||
@@ -72,28 +79,25 @@ var srcFiles = true; | ||
var qualified = name + ':' + target; | ||
var stamp = util.getStampPath(options.cache, name, target); | ||
var repeat = grunt.file.exists(stamp); | ||
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); | ||
} | ||
if (!repeat) { | ||
/** | ||
* This task has never succeeded before. Process everything. This is | ||
* less efficient than it could be for cases where some dest files were | ||
* created in previous runs that failed, but it makes things easier. | ||
*/ | ||
grunt.task.run([ | ||
qualified + (args ? ':' + args : ''), | ||
'newer-postrun:' + qualified + ':-1:' + options.cache | ||
]); | ||
return; | ||
function override(filePath, time, include) { | ||
var details = { | ||
task: taskName, | ||
target: targetName, | ||
path: filePath, | ||
time: time | ||
}; | ||
options.override(details, include); | ||
} | ||
// This task has succeeded before. Filter src files. | ||
var done = this.async(); | ||
var previous = fs.statSync(stamp).mtime; | ||
var files = grunt.task.normalizeMultiTaskFiles(config, target); | ||
util.filterFilesByTime(files, previous, function(err, newerFiles) { | ||
if (err) { | ||
return done(err); | ||
var files = grunt.task.normalizeMultiTaskFiles(config, targetName); | ||
util.filterFilesByTime(files, previous, override, function(e, newerFiles) { | ||
if (e) { | ||
return done(e); | ||
} else if (newerFiles.length === 0) { | ||
@@ -118,3 +122,4 @@ grunt.log.writeln('No newer files to process.'); | ||
delete config.dest; | ||
grunt.config.set([name, target], config); | ||
grunt.config.set([taskName, targetName], config); | ||
// because we modified the task config, cache the original | ||
@@ -124,2 +129,3 @@ var id = cacheConfig(originalConfig); | ||
// run the task, and attend to postrun tasks | ||
var qualified = taskName + ':' + targetName; | ||
var tasks = [ | ||
@@ -153,18 +159,18 @@ qualified + (args ? ':' + args : ''), | ||
var internal = 'Internal task.'; | ||
grunt.registerTask( | ||
'newer-postrun', 'Internal task.', function(name, target, id, dir) { | ||
'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, name, target), ''); | ||
grunt.file.write(util.getStampPath(dir, taskName, targetName), ''); | ||
// reconfigure task if modified config was set | ||
if (id !== '-1') { | ||
grunt.config.set([name, target], pluckConfig(id)); | ||
} | ||
// reconfigure task with original config | ||
grunt.config.set([taskName, targetName], pluckConfig(id)); | ||
}); | ||
var clean = 'Remove cached timestamps.'; | ||
grunt.registerTask( | ||
'newer-clean', 'Remove cached timestamps.', function(name, target) { | ||
'newer-clean', clean, function(taskName, targetName) { | ||
var done = this.async(); | ||
@@ -177,6 +183,6 @@ | ||
var cacheDir = path.join(__dirname, '..', '.cache'); | ||
if (name && target) { | ||
cacheDir = util.getStampPath(cacheDir, name, target); | ||
} else if (name) { | ||
cacheDir = path.join(cacheDir, name); | ||
if (taskName && targetName) { | ||
cacheDir = util.getStampPath(cacheDir, taskName, targetName); | ||
} else if (taskName) { | ||
cacheDir = path.join(cacheDir, taskName); | ||
} | ||
@@ -183,0 +189,0 @@ if (grunt.file.exists(cacheDir)) { |
@@ -39,5 +39,5 @@ var assert = require('assert'); | ||
if (expected.length === 0) { | ||
assert.equal(log.length, 0, 'No log entries'); | ||
assert.equal(log.length, 0, 'Expected no log entries, got ' + log.length); | ||
} else { | ||
assert.equal(log.length, 1, 'One log entry'); | ||
assert.equal(log.length, 1, 'Expected one log entry, got ' + log.length); | ||
var actual = log[0]; | ||
@@ -44,0 +44,0 @@ assert.deepEqual(actual, expected); |
@@ -9,2 +9,6 @@ var mock = require('mock-fs'); | ||
function nullOverride(filePath, time, include) { | ||
include(false); | ||
} | ||
describe('filterPathsByTime()', function() { | ||
@@ -39,3 +43,4 @@ | ||
util.filterPathsByTime(paths, new Date(150), function(err, results) { | ||
util.filterPathsByTime(paths, new Date(150), nullOverride, | ||
function(err, results) { | ||
if (err) { | ||
@@ -51,2 +56,55 @@ return done(err); | ||
it('calls override with older files and comparison time', function(done) { | ||
var paths = [ | ||
'src/js/a.js', | ||
'src/js/b.js', | ||
'src/js/c.js' | ||
]; | ||
function customOverride(filePath, time, include) { | ||
assert.equal(filePath, 'src/js/a.js'); | ||
assert.equal(time.getTime(), 150); | ||
include(false); | ||
} | ||
util.filterPathsByTime(paths, new Date(150), customOverride, | ||
function(err, results) { | ||
if (err) { | ||
return done(err); | ||
} | ||
assert.equal(results.length, 2); | ||
assert.deepEqual(results.sort(), ['src/js/b.js', 'src/js/c.js']); | ||
done(); | ||
}); | ||
}); | ||
it('allows override to force inclusion of older files', function(done) { | ||
var paths = [ | ||
'src/js/a.js', | ||
'src/js/b.js', | ||
'src/js/c.js' | ||
]; | ||
function customOverride(filePath, time, include) { | ||
assert.equal(filePath, 'src/js/a.js'); | ||
assert.equal(time.getTime(), 150); | ||
include(true); | ||
} | ||
util.filterPathsByTime(paths, new Date(150), customOverride, | ||
function(err, results) { | ||
if (err) { | ||
return done(err); | ||
} | ||
assert.equal(results.length, 3); | ||
assert.deepEqual(results.sort(), | ||
['src/js/a.js', 'src/js/b.js', 'src/js/c.js']); | ||
done(); | ||
}); | ||
}); | ||
it('calls callback error if file not found', function(done) { | ||
@@ -58,3 +116,4 @@ | ||
util.filterPathsByTime(paths, new Date(150), function(err, results) { | ||
util.filterPathsByTime(paths, new Date(150), nullOverride, | ||
function(err, results) { | ||
assert.instanceOf(err, Error); | ||
@@ -97,3 +156,3 @@ assert.equal(results, undefined); | ||
it('calls callback with true if any file is newer', function(done) { | ||
util.anyNewer(paths, new Date(250), function(err, newer) { | ||
util.anyNewer(paths, new Date(250), nullOverride, function(err, newer) { | ||
if (err) { | ||
@@ -107,4 +166,18 @@ return done(err); | ||
it('does not call override if all files are newer', function(done) { | ||
function override(filePath, time, include) { | ||
done(new Error('Override should not be called')); | ||
} | ||
util.anyNewer(paths, new Date(1), override, function(err, newer) { | ||
if (err) { | ||
return done(err); | ||
} | ||
assert.isTrue(newer); | ||
done(); | ||
}); | ||
}); | ||
it('calls callback with false if no files are newer', function(done) { | ||
util.anyNewer(paths, new Date(350), function(err, newer) { | ||
util.anyNewer(paths, new Date(350), nullOverride, function(err, newer) { | ||
if (err) { | ||
@@ -118,4 +191,35 @@ return done(err); | ||
it('calls override with older file and time', function(done) { | ||
function override(filePath, time, include) { | ||
assert.equal(filePath, 'src/js/a.js'); | ||
assert.equal(time.getTime(), 150); | ||
include(false); | ||
} | ||
util.anyNewer(paths, new Date(150), override, function(err, newer) { | ||
if (err) { | ||
return done(err); | ||
} | ||
assert.isTrue(newer); | ||
done(); | ||
}); | ||
}); | ||
it('allows override to force inclusion of older files', function(done) { | ||
function override(filePath, time, include) { | ||
include(true); | ||
} | ||
util.anyNewer(paths, new Date(1000), override, function(err, newer) { | ||
if (err) { | ||
return done(err); | ||
} | ||
assert.isTrue(newer); | ||
done(); | ||
}); | ||
}); | ||
it('calls callback with error if file not found', function(done) { | ||
util.anyNewer(['bogus/file.js'], new Date(350), function(err, newer) { | ||
util.anyNewer(['bogus/file.js'], new Date(350), nullOverride, | ||
function(err, newer) { | ||
assert.instanceOf(err, Error); | ||
@@ -170,3 +274,4 @@ assert.equal(newer, undefined); | ||
}]; | ||
util.filterFilesByTime(files, new Date(1000), function(err, results) { | ||
util.filterFilesByTime(files, new Date(1000), nullOverride, | ||
function(err, results) { | ||
assert.isNull(err); | ||
@@ -187,3 +292,4 @@ assert.equal(results.length, 1); | ||
}]; | ||
util.filterFilesByTime(files, new Date(1000), function(err, results) { | ||
util.filterFilesByTime(files, new Date(1000), nullOverride, | ||
function(err, results) { | ||
assert.isNull(err); | ||
@@ -210,3 +316,4 @@ assert.equal(results.length, 1); | ||
}]; | ||
util.filterFilesByTime(files, new Date(150), function(err, results) { | ||
util.filterFilesByTime(files, new Date(150), nullOverride, | ||
function(err, results) { | ||
assert.isNull(err); | ||
@@ -230,3 +337,4 @@ assert.equal(results.length, 2); | ||
}]; | ||
util.filterFilesByTime(files, new Date(200), function(err, results) { | ||
util.filterFilesByTime(files, new Date(200), nullOverride, | ||
function(err, results) { | ||
assert.isNull(err); | ||
@@ -249,3 +357,4 @@ assert.equal(results.length, 1); | ||
}]; | ||
util.filterFilesByTime(files, new Date(1000), function(err, results) { | ||
util.filterFilesByTime(files, new Date(1000), nullOverride, | ||
function(err, results) { | ||
assert.isNull(err); | ||
@@ -265,3 +374,4 @@ assert.equal(results.length, 1); | ||
}]; | ||
util.filterFilesByTime(files, new Date(1000), function(err, results) { | ||
util.filterFilesByTime(files, new Date(1000), nullOverride, | ||
function(err, results) { | ||
assert.instanceOf(err, Error); | ||
@@ -268,0 +378,0 @@ assert.isUndefined(results); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
55394
44
1509
175
+ Addedasync@0.2.10(transitive)
+ Addedrimraf@2.2.6(transitive)
- Removedasync@0.2.9(transitive)
- Removedgraceful-fs@2.0.3(transitive)
- Removedrimraf@2.2.4(transitive)
Updatedasync@0.2.10
Updatedrimraf@2.2.6