Renaming Grunt NPM Tasks

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.

Did something I wrote help you out?

That's great! I don't earn any money from this site - I run no ads, sell no products and participate in no affiliate programs. I do this solely because it's fun; I enjoy writing and sharing what I learn.

All the same, if you found this article helpful and want to show your appreciation, here's my Amazon.com wishlist.