Another common requirement for development environments is the need to automatically run specific tasks when certain files are changed. This is especially useful when you'd like to monitor the quality of changing code in real time, or recompile altered resources as soon as they change, so that the effect of the changes are reflected without any manual intervention.
The contirb-watch
(0.6.1)
plugin allows us to keep a watch on a specific set of files, and run a specified set of tasks when file events are observed.
In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.
The following steps take us through setting up a watch
task that initiates a code quality analysis on a JavaScript source file each time a change to the file is observed.
We'll start by installing the package that contains the
contrib-watch
plugin and loading its tasks by following the instructions provided in the Installing a Plugin recipe of this chapter.For our example, we'll make use of the
jshint
task to analyze the quality of a JavaScript source file. Let's install thecontrib-jshint
plugin and load its tasks by following the instructions provided in the Installing a Plugin recipe of this chapter.We'll also need a JavaScript source file that we can watch for changes and perform a quality analysis on. Let's create a file called
sample.js
in our project root and provide it with the following contents:var sample = 'Sample'; console.log(sample);
Now, we can set up the example
jshint
task, which we'll run using thewatch
task by adding the following to our configuration:jshint: { sample: { src: ['sample.js'] } }
With the plugin installed and the sample task configured, we can now configure a target on the
watch
task, which will run thejshint
task every time the sample file calledsample.js
changes. This is done by adding the following to our configuration:watch: { sample: { files: ['sample.js'], tasks: ['jshint'] } }
Finally, we can start the task using the
grunt watch
command, which should produce the following output to confirm that it's running:Running "watch" task Waiting...
To test our setup, we can now make some changes to the
sample.js
file and save them. This should produce output informing us of the file event similar to the following:Running "watch" task Waiting... >> File "sample.js" changed. Running "jshint:sample" (jshint) task >> 1 file lint free. Done, without errors. Completed in 1.0s at Wed Jan 1 2014 00:00:00 GMT - Waiting...
The watch
task plugin provides many more useful configuration options that allow us to watch more than one file, run a series of tasks, prevent process spawning for task runs, enable the interruption of task runs, specify the waiting period before rerunning tasks, run tasks only on specific file events, allow tasks to kill the watcher process, and run tasks once when the watcher starts up.
In case we'd like to watch more than one file, the pattern-matching capabilities of the standard Grunt files
configuration can be used. The following configuration example will observe all the files in the project root or any of its sub directories with the txt
extension:
watch: { sample: { files: ['**/*.txt'], tasks: ['sample'] } }
In case we'd like to run more than one task each time a file event is observed, we can just add the tasks to the array passed to the tasks
configuration:
watch: { sample: { files: ['sample.txt'], tasks: ['sample', 'another', 'finally'] } }
The default behavior of the watch
task is to start each of the triggered tasks in their own child process. This prevents failing triggered tasks from causing the watch
task itself to fail. As a side effect, it also clones the context of the watcher process for each task. This behavior can however be disabled by setting the spawn
option to false
, which triggers tasks a little faster and allows them to share a context between them. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { spawn: false } } }
The default behavior for the watcher is to wait for the completion of the tasks triggered by the previous change, before waiting for changes again. By setting the interrupt
option to true
, the watcher will stop running tasks when a change is detected and start rerunning them. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { interrupt: true } } }
The default period the watcher will wait before checking for file changes after a previous task run is 500ms
. This amount of time can be changed by setting the debounceDelay
option to the number of milliseconds you'd like for it to wait. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { debounceDelay: 1000 } } }
In addition to being changed, files can also be added and deleted. The default behavior of the watcher is to observe all these events, but if you'd like it to run tasks only on specific events, the event
option can be set to either changed
, added
, deleted
, or all
.
The following example will only start the sample
task if a file named sample.txt
is added or deleted to the same path as the configuration file:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { event: ['added', 'deleted'] } } }
Warnings and failures that are raised by the tasks started by the watch task will, by default, not interrupt its execution. Setting the forever
option to false
will disable this behavior and allow child tasks to stop the watcher process on warnings and failures. The following demonstrates the configuration for this:
watch: { options: { forever: false }, sample: { files: ['sample.txt'], tasks: ['sample'] } }
In the case that you'd like to run the tasks specified in the tasks
option once the watcher starts up, and not only once file events are observed, you can set the atBegin
option to true
. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { atBegin: true } } }