Building Node.js Apps with Grunt
Last updated May 15, 2024
Table of Contents
Grunt is a generic task-runner for Node.js projects with a huge library of plugins (grunt-contrib-*). By automating tasks, you can streamline processes for your project (and reduce human error on your team).
This article walks through automating Node.js builds with Grunt on Heroku. You can follow along with your own project, or clone our example from GitHub.
Provide a Gruntfile in your project
Grunt is configured via Gruntfile.js
, which should exist at the root of your project alongside package.json
. For this example, we’ll use a simple Gruntfile:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/factorial.js',
dest: 'build/factorial.min.js'
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};
This configuration provides a single task: uglify
, which will build src/factorial.js
into a minified build/factorial.min.js
.
Install Grunt and necessary plugins
You should always install npm modules locally (without the -g, or global, option), so that all of your project’s dependencies are tracked in package.json
. All grunt projects will need grunt and grunt-cli, which you can install like this:
$ npm install grunt grunt-cli
You’ll also need to install any Grunt plugins used by your project. For this example, we’re using grunt-contrib-uglify
:
$ npm install --save-exact grunt-contrib-uglify
Specify your Grunt task in a build
script
To use Grunt as a build tool, we need the uglify task to be run after npm modules have been installed, but before we start our app. That’s exactly what the build
script in the package.json
does when deploying on Heroku. Add the following to your package.json
:
"scripts": {
"build": "grunt uglify",
"start": "node server.js"
}
Ignore generated files
You shouldn’t track generated files in git; since they depend on already-tracked files, it’s just a recipe for collisions. Add any generated paths to .gitignore
so your Grunt tasks don’t trigger changes in your source control:
node_modules
build/*.js
Check the build
You can easily run your automated workflow locally. If you don’t have a project set up, feel free to clone the example:
$ git clone https://github.com/heroku-examples/node-grunt.git
$ cd node-grunt
Now, ensure that the build
script generates a minified file in /build
:
$ npm install
$ npm run build
> node-grunt@1.0.0 build /Users/jmorrell/workspace/node-grunt
> grunt uglify
Running "uglify:build" (uglify) task
>> 1 file created.
Done, without errors.
Nice!
Deploy to Heroku
$ git push heroku main
If you watch the build output, you’ll see the Grunt task running:
remote: Running build
remote:
remote: > node-grunt@1.0.0 build /tmp/build_e414c7cff23f45169616765eae55c51e
remote: > grunt uglify
remote:
remote: Running "uglify:build" (uglify) task
remote: >> 1 file created.
remote:
remote: Done, without errors.
Summary
Adding grunt functionality is simple: just use the build
hook to ensure a grunt task gets executed as part of the build process.