# Introduction to Grunt

Source: https://tpiros.dev/blog/introduction-to-grunt

I'd been wanting to try [Grunt](http://gruntjs.com/) for a while. So what is it? Grunt is a JavaScript Task Runner. It lets developers automate repetitive tasks in an easy, convenient way. The best explanation comes straight from Grunt's site:

> The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes.

Hard to argue with that. Developers are lazy (in the best sense). We don't want to do repetitive things that should be automated.

Think about how many times you've:

- merged JavaScript files by hand?
- minified JavaScript files through some web service?
- compiled your LESS files?
- left `console.log` messages in production code?

If you're nodding, you've hit at least one of those. I know I have, multiple times across different projects.

Grunt automates all of the above. Let me walk you through it step by step with a dummy project.

First, set up a `package.json` file using `npm init`.

_Quick aside: `npm` does **not** stand for "node package manager", even though that'd make sense. The letters were chosen because they're easy to type. Don't believe me? Check [npm's FAQ](https://npmjs.org/doc/faq.html)._

`npm init` creates the `package.json` file by asking a few questions about the project: name, version number, author, and so on. Leave most values at their defaults since we'll be modifying the file anyway.

You should end up with something like this:

```json
{
  "name": "grunt-intro",
  "version": "0.0.0",
  "description": "Introduction to Grunt",
  "repository": {
    "type": "git",
    "url": "https://github.com/tpiros/grunt-intro.git"
  },
  "author": "Tamas Piros",
  "license": "BSD"
}
```

Let's also create two "dummy" JavaScript files under the `js` directory:

```js
function Example() {}

Example.prototype.methodOne = function (number) {
  console.log('Function argument is set to: ' + number);
  var result;
  if (number === 0) {
    result = 'Zero.';
  } else {
    result = 'Not zero.';
  }
  return result;
};

Example.prototype.methodTwo = function (numberOne, numberTwo) {
  console.log('First argument: ' + numberOne);
  console.log('Second argument: ' + numberTwo);

  var add = numberOne + numberTwo;
  return add;
};

module.exports = Example;

function AnotherExample() {}

AnotherExample.prototype.methodOne = function (number) {
  console.log('Function argument is set to: ' + number);
  var result;
  result = number * number;
  return result;
};

AnotherExample.prototype.methodTwo = function (numberOne, numberTwo) {
  console.log('First argument: ' + numberOne);
  console.log('Second argument: ' + numberTwo);

  var divide = numberOne / numberTwo;
  return divide;
};

module.exports = AnotherExample;
```

Two random files I knocked together in about two minutes. I've named them `example.js` and `example2.js`.

To get these production-ready, we need to:

- merge the two files into one
- remove all logging statements
- minify the final, merged file

First, install Grunt itself:

```
npm install -g grunt-cli
```

This puts `grunt` on the system path (the `-g` flag makes it globally executable). Since version 0.4, `grunt-cli` doesn't install the grunt task runner itself. It just ensures the right version runs.

Now extend the `package.json` by adding a `devDependencies` section. We won't do this manually. Let `grunt-cli` handle it:

```
npm install grunt --save-dev
```

This installs the grunt task runner and appends `package.json` with the following (thanks to `--save-dev`):

```json
"devDependencies": {
  "grunt": "~0.4.2",
}
```

Good so far. Grunt's installed. Time to create its configuration file, `Gruntfile.js`, which holds all the automation logic.

The skeleton:

```js
module.exports = function (grunt) {
  //automation logic goes in here
};
```

Let's hook `Gruntfile.js` up with `package.json`. This is a great feature because we can read variables from the package file right into the grunt file (you'll see what I mean in a moment). Let's extend the code and add our first automation: merging JavaScript files.

```js
module.exports = function (grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      dist: {
        src: ['js/*.js'],
        dest: 'js/build/allexamples.js',
      },
    },
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.registerTask('default', ['concat']);
};
```

We read `package.json` and add the `concat` task for merging files. `src` defines which files to merge (you can list them individually or use a wildcard). `dest` is the output location. If the path doesn't exist, Grunt creates it.

Look at the last two lines. The first loads the npm module, which we need to install:

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

(The `--save-dev` flag extends `devDependencies`:)

```json
"devDependencies": {
  "grunt": "~0.4.2",
  "grunt-contrib-concat": "~0.3.0"
}
```

Finally, we register the task. This lets us run just `grunt` from the command line instead of specifying tasks individually like `grunt default` and `grunt concat`.

Run `grunt` from the command line. If everything's right, you'll find `allexamples.js` under `/js/build/` with the contents of both JavaScript files.

Brilliant so far. Let's go further and minify our new JavaScript. We'll use the uglify task. Same pattern: install the npm module first:

```
npm install grunt-contrib-uglify --save-dev
```

This adds another entry to `devDependencies`.

Then add the configuration to `Gruntfile.js`:

```js
uglify: {
  files: {
    src: 'js/build/allexamples.js',
    dest: 'js/build/',
    expand: true,
    flatten: true,
    ext: '.min.js'
  }
}
```

**Note** If you have multiple task configurations, separate them with a `,` (comma) like a standard `JSON` file.

Similar to concat: we tell Grunt the source, destination, and extension. (There are many more uglify options. Check the [documentation](https://github.com/gruntjs/grunt-contrib-uglify#grunt-contrib-uglify-v027-) for details.)

Don't forget to load the npm task and update `registerTask`:

```
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['concat', 'uglify']);
```

**Note** The order of tasks in the array matters. Grunt runs them sequentially. If you uglified first and then concatenated, the final file would be merged but not minified.

Run `grunt` again. You should now have `allexamples.min.js` in the `js/build/` folder.

Last step: strip out all `console.log()` statements. By now you know the drill. Install, load, register.

```
npm install grunt-remove-logging --save-dev
```

```js
removelogging: {
  dist: {
    src: "js/build/allexamples.js",
    dest: "js/build/allexamples.js"
  }
}

grunt.loadNpmTasks('grunt-remove-logging');
grunt.registerTask('default', ['concat', 'removelogging', 'uglify']);
```

Run `grunt` one last time. You should end up with a JavaScript file like this:

```
/*! grunt-intro 2013-11-27 */
function Example(){}function AnotherExample(){}Example.prototype.methodOne=function(a){var b;return b=0===a?"Zero.":"Not zero."},Example.prototype.methodTwo=function(a,b){var c=a+b;return c},module.exports=Example,AnotherExample.prototype.methodOne=function(a){var b;return b=a*a},AnotherExample.prototype.methodTwo=function(a,b){var c=a/b;return c},module.exports=AnotherExample;
```

You've probably noticed the JavaScript comment on the first line. That comes from one of uglify's options:

```
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
```

Remember how we hooked up `package.json` and `Gruntfile.js`? Here we're using a template with `<%= %>` tags to pull the `name` value from `package.json`.

JavaScript's sorted. One more thing. I really don't want to compile LESS files by hand. Grunt handles that too. Same steps as before:

```
npm install grunt-contrib-less --save-dev
```

```js
less: {
  development: {
    files: {
      "css/style.css": "less/style.less"
    }
  }
}

grunt.loadNpmTasks('grunt-contrib-less');
grunt.registerTask('default', ['concat', 'removelogging', 'uglify', 'less']);
```

My example LESS file gets transformed into CSS:

```css
@base: #f938ab;

.box-shadow(@style, @c) when (iscolor(@c)) {
  -webkit-box-shadow: @style @c;
  -moz-box-shadow: @style @c;
  box-shadow: @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
  .box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box {
  color: saturate(@base, 5%);
  border-color: lighten(@base, 30%);
  div {
    .box-shadow(0 0 5px, 30%);
  }
}

.box {
  color: #fe33ac;
  border-color: #fdcdea;
}
.box div {
  -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
  -moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
```

That's a basic run through Grunt. It can do a lot more, but I hope this helps anyone getting started. Grunt saves developers real time on tasks that come up again and again.

If you had trouble following along, I've committed the examples to [GitHub](https://github.com/tpiros/grunt-intro). Check the repository and the [README file](https://github.com/tpiros/grunt-intro/blob/master/README.md) for setup instructions.
