You don't need global grunt

Building projects without having Grunt installed globally.

Grunt is an excellent build tool for JavaScript projects. It is simple to get started, has tonnes of plugins, but requires a global installation.

npm install -g grunt-cli

I hate requiring a global tool just to build something. Each of my Travis projects has to install grunt-cli before running unit tests

before_script:
  - npm install -g grunt-cli

It is hard to enforce a specific global grunt version too. Can I avoid having to install Grunt globally? There are several ways one can get around having to install grunt-cli

grunt-cli as devDependency

Instead of relying on the global grunt-cli, we can install it as a project's dev dependency.

npm install --save-dev grunt-cli

then we can connect NPM scripts to run the grunt commands

package.json
1
2
3
4
5
6
{
"scripts": {
"build": "grunt",
"test": "grunt test"
}
}
$ npm run build
> [email protected] build /git/ng-describe
> grunt
>> Local Npm module "grunt-cli" not found. Is it installed?
Running "nice-package" task
package.json fixed!
...

grunt-cli gives a warning (above), but runs just fine. If we use Node version > 0.10 we can even pass arguments to the grunt executable

$ npm run build -- --verbose
> [email protected] build /git/ng-describe
> grunt --verbose
Initializing
Command-line options: --verbose
...

Skip grunt completely

I also hate writing Gruntfile.js - too much boilerplate, and I never remember which plugins I need to load, etc. Here is a way around it.

For some projects, you might not need many plugins, and instead connect the individual grunt plugins (like the very good grunt-contrib-concat) to NPM scripts via grunty. Grunty fakes grunt file to run a plugin directly.

npm install --save-dev grunty grunt-contrib-concat

To concatenate a couple of files into a desired file, instead of writing a Gruntfile.js, just specify input and output options in the package's script command

package.json
1
2
3
"scripts": {
"concat": "grunty grunt-contrib-concat concat --src=a.js,b.js --dest=dist/out.js"
}

You can even place the plugin's configuration into a JSON or JavaScript file for simplicity

concat.json
1
2
3
4
5
6
7
8
{
"concat": {
"default": {
"src": ["test/a.js", "test/b.js"],
"dest": "test/out-json-config.js"
}
}
}

and use the configuration filename instead of the separate arguments

package.json
1
2
3
"scripts": {
"concat": "grunty grunt-contrib-concat concat concat.json"
}