How do you watch multiple files, but only run task on changed file, in Grunt.js?
In learning how to use grunt, I am trying to make a simple coffee-script watcher/compiler. The problem is, if I tell the watch
task to watch several files, and one changes, it is going to pass all of the files to the coffee
command. This means when you change 1 file, it's going to recompile all of the files matching the src
pattern. Instead, I want to only recompile the single file that changed that matches the src
pattern.
Here is the grunt.js
:
module.exports = function(grunt) {
grunt.initConfig({
coffee: {
app: {
src: ['test/cases/controller/*.coffee'],
dest: 'tmp',
options: {
bare: true,
preserve_dirs: true
}
}
},
watch: {
files: ['<config:coffee.app.src>'],
tasks: ['coffee:app']
}
});
grunt.loadNpmTasks('grunt-coffee');
grunt.registerTask('default', 'coffee');
};
This is using grunt-coffee, which is basically this: https://gist.github.com/2373159.
When I run grunt watch
, and I save a file in test/cases/controller/*.coffee
, it compiles all of the matching files (putting them in tmp/*
).
How do you instead only compile the changed file using grunt?
The upcoming (and currently in-development) v0.4.0a grunt has the grunt.file.watchFiles object, which was designed expressly for this purpose. The grunt-coffee plugin may have added support for this feature already, I'm not sure.
Either way, if you're interested in trying an in-development version of grunt in your project, check out the When will I be able to use in-development feature 'X'? FAQ entry.
I got this working when compiling my less files. You should be able to mess with this configuration a little bit to git it working with the coffeescript plugin. The portion of interest is the grunt.event.on('watch', ...)
. In this event handler I'm updating the files
property in the less command to only contain the changed file.
path = require('path');
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
less: {
development: {
options: {
paths: ["./library/less"],
},
files: [
{ src: "./library/less/bootstrap.less", dest: "./library/css/bootstrap.css"},
{ src: "./library/less/app.less", dest: "./library/css/app.css"}
]
}
},
watch: {
styles: {
files: "./library/less/*",
tasks: ["less"],
options: {
nospawn: true,
},
},
},
});
// Event handling
grunt.event.on('watch', function(action, filepath){
// Update the config to only build the changed less file.
grunt.config(['less', 'development', 'files'], [
{src: filepath, dest: './library/css/' + path.basename(filepath, '.less') + '.css'}
]);
});
// Load the plugins
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
// Tasks
grunt.registerTask('default', ['watch']);
};
None of these answers worked very well for me. Here is my solution if anyone is interested (I know I'm a little late in replying to this question).
https://gist.github.com/ryansmith94/8569178
In this issue, Kyle Robinson suggests using the watch
event. It's very important to set watch task nospawn
property to true
to make it work. I modified his solution to selectively run the tasks:
grunt.event.on('watch', function(action, filepath) {
if (minimatch(filepath, grunt.config('watch.stylesheets.files'))) {
grunt.config('compass.dist.options.specify', [filepath]);
}
if (minimatch(filepath, grunt.config('watch.scripts.files'))) {
var uglifySrc = filepath.replace(grunt.config('uglify.dist.cwd'), '');
grunt.config('jshint.dist.src', [filepath]);
grunt.config('uglify.dist.src', [uglifySrc]);
}
});
Here is the complete solution: https://gist.github.com/luissquall/5408257