For the last few years, Gulp has been my go-to task runner for Node projects and, generally, anywhere where I need to build things or run tasks. But the recent release of Gulp 4 broke all of my config files and left me with hours of frustrating rewrites, I decided to see what else might be out there. And, naturally, I landed on Grunt.
One thing I liked about Gulp (prior to 4.0) was it’s much looser structure that allowed a lot of freedom in how you structured your file. Grunt seems to be much more structured and opinionated. And sometimes, I don’t like those opinions.
A prime example of this is grunt-contrib-watch
. When I type grunt watch
, I
want to run a series of setup tasks first before firing the watcher up. But
grunt-contrib-watch
squats on the prime real estate that is the watch
command.
But I wanted to use that command. And there doesn’t seem to be any way to just
say “run these arbitrary tasks before starting the watcher.” At least not one
that I could find clearly documented. Sure, I could just make my own mywatch
or similar command, but I’m picky. I want my command, so we need a way to rename
it.
So how can I get grunt-contrib-watch
to quit squatting on the watch
command,
but still maintain it’s functionality? Well, grunt provides a renameTask
command. But simply calling that on watch
that is specified in initConfig
results in an endless loop. So we have to get creative:
grunt.registerTask('watch', function() {
grunt.renameTask('watch', 'foo');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.config.set('watch', {
build: {
files: "src/foo",
tasks: 'build'
}
});
grunt.task.run('build:full');
grunt.task.run('docker:up');
grunt.task.run('watch');
});
See what happened there? We create our own watch
task. Then, the first thing
we do when it is called is have it rename itself. What we rename it to doesn’t
matter because it’s disposable. Watch is a one-shot command, and we’re not going
to execute it again in this run.
But once we’ve renamed it, we load in grunt-contrib-watch
, add it’s config to
Grunt like it always was there. Run whatever arbitrary tasks we need to run (an
initial full build, bringing Docker containers up, etc.) Then, when we’re done,
we call watch again, which now points to the correct watcher.
I have to admit, even I was impressed that this actually worked.