Grunt, automating repetitive work

12/04/2017
Grunt, automating repetitive work

Grunt is a Javascript task automator that allows us to launch a series of tasks at the same time with just a single order. Being based on Javascript, both the syntax and the operation is very simple, working based on plugins

Instalation

npm install -g grunt-cli

 

We will need two main files that we will place in the root of the project in which Grunt will perform its tasks:

package.json

{
  "name": "ExampleProject",
  "version": "0.1",
  "dependencies": {
   
  },
  "devDependencies": {

  }
}

and Gruntfile.js

module.exports = function(grunt){
  grunt.initConfig({

 
 });
}

As you see them now is the basic state of the two files. The package.json file is responsible for the package dependencies for npm to help us manage those dependencies and versions of our application, so running a simple `npm install 'in the directory where this file is located would install all Packages referenced in it (and its secondary dependencies).

Gruntfile.js is the Grunt configuration file. In it we will specify the plugins, the configuration of each one of them and the tasks to execute in each case. Inside the grunt.config ({}) we specify the configuration of the tasks; If we are using a plugin, it must be loaded, outside the grunt.config with the function grunt.loadNpmTasks (); And the tasks are specified with grunt.registerTask (). Let's look at some examples:

Plugins

Reviewing the basic repetitive tasks we can find in almost any project we find: Sass compilation, Compass, CoffeeScript compilation, Javascript compression and resulting CSS, execute some command (such as 'drush cc all') and many others.

grunt-contrib-compass

The compass plugin is a must for us to compile and set Compass settings in different environments.

npm install grunt-contrib-compass --save-dev

 

Whenever we save with --save-dev, it will save the dependency on our package.json itself.

Within our Gruntfile.js:

module.exports = function(grunt){
  grunt.initConfig({
    compass: {
        dist: {
          sassDir: ['sass'],
          cssDir: ['css'],
          environment: 'production'
        },
        dev: {
          sassDir: ['sass'],
          cssDir: ['css'],
          environment: 'development'
        },
    }
  });
  grunt.loadNpmTasks('grunt-contrib-compass');
  grunt.registerTask('dev', ['compass:dev']);
  grunt.registerTask('prod', ['compass:dist']);
}

We have set two tasks with grunt.registerTask (): dev and prod, one for each environment that we have specified in our setup a bit higher.

  grunt.registerTask('dev', ['compass:dev']);
  grunt.registerTask('prod', ['compass:dist']);

If we want to compass Compass for development, we would just have to run a 'grunt dev' from the terminal; Or 'grunt prod' to run the production compilation. Since we only have the tasks corresponding to grunt-contrib-compass right now, we can not add more, but usually a task will not only involve one action, but several more, as we will see below.

grunt-contrib-coffee

With CoffeeScript we find the same situation as with compass: we can have a process with a 'coffee -woven' to compile and wait for changes, or we can include it in our grunt and disengage ourselves from it. To install it, there is no more to execute.

npm install grunt-contrib-coffee --save-dev

It is a tremendously complete plugin with many options and allows you to concatenate, expand on files, create Sourcemaps and so on.

...

coffee: {
  compile: {
    files: {
        // 1:1 compile
      'path/to/result.js': 'path/to/source.coffee', 
        // compile and concat into single file
      'path/to/another.js': ['path/to/sources/*.coffee', 'path/to/more/*.coffee'] 
    }
  },
  compileWithMaps: {
    options: {
      sourceMap: true
    },
    files: {
      'path/to/result.js': 'path/to/source.coffee', 
      'path/to/another.js': ['path/to/sources/*.coffee', 'path/to/more/*.coffee']
    }
  },
  glob_to_multiple: {
    expand: true,
    flatten: true,
    cwd: 'path/to',
    src: ['*.coffee'],
    dest: 'path/to/dest/',
    ext: '.js'
  }
}

...

grunt.loadNpmTasks('grunt-contrib-coffee');

We also update our tasks:

  grunt.registerTask('dev', ['compass:dev','coffee:compileWithMaps']);
  grunt.registerTask('prod', ['compass:dist','coffee:compile']);

Now when we run 'grunt dev', Compass will first compile in development and then Coffee with Sourcemaps. But what happens when we make changes?

grunt-contrib-watch

In a development environment, an automatic compilation is always good as we make changes so that we do not have to continually stop to compile and test. This can be done thanks to 'watch', something that usually we have in compass ('watch') but that here we extend to modifications of JS or CoffeeScript.

npm install grunt-contrib-watch --save-dev

In our configuration we specify what happens when you see a change; In our case we will differentiate between configuration changes, changes in JS-Coffee, and changes in Sass (Compass) files.

...
watch: {
  configFiles: {
    files: [ 'Gruntfile.js', 'config/*.js' ],
    options: {
      reload: true
    }
  },
  css: {
    files: ['sass/**/**/**'],
    tasks: ['compass:dev'],
    options: {
        spawn: false
    }
  },
  js: {
    files: ['coffee/**/**/**'],
    tasks: ['coffee:compileWithMaps'],
    options: {
        spawn: false
    }
  }
},
...
grunt.loadNpmTasks('grunt-contrib-watch');
...
grunt.registerTask('dev', ['compass:dev','coffee:compileWithMaps','watch']);

In this way, Grunt monitors changes in its own configuration to recharge if necessary and monitors changes in coffee and sass.

grunt-contrib-shell

This goes for note: what if we want to launch a command in the terminal automatically with grunt? What if we want it to be launched every time our watch detects a change? For that we have grunt-shell:

npm install grunt-shell --save-dev

In our case it is quite common to run a 'drush cc all' or 'drush cc css-js' every time you make changes to a portal where you already have the general Drupal cache enabled. Let's see how we configure it:

...
    shell: {
      'options': {
        'stdout': true,
        'stderr': true,
        'failOnError': true
      },
      drushall: {
        command: 'drush cc all',
      },
      drushcssjs: {
        command: 'drush cc css-js',
      }
    },
...

  grunt.loadNpmTasks('grunt-shell');

We have set up two task settings: one to clear the general cache, and one to clear the css-js of the theme; The first one is executed when grunt is started, and the second one is executed when we detect changes with watch.

 

Here you have the file resulting from all this: something that covers the most common areas of a project and that will help us to ignore those tedious repetitive tasks. 

module.exports = function(grunt){
  grunt.initConfig({
    shell: {
      'options': {
        'stdout': true,
        'stderr': true,
        'failOnError': true
      },
      drushall: {
        command: 'drush cc all',
      },
      drushcssjs: {
        command: 'drush cc css-js',
      }
    },
    coffee: {
      compile: {
        files: {
          'path/to/result.js': 'path/to/source.coffee', 
          'path/to/another.js': ['path/to/sources/*.coffee', 'path/to/more/*.coffee'] 
        }
      },
      compileWithMaps: {
        options: {
          sourceMap: true
        },
        files: {
          'path/to/result.js': 'path/to/source.coffee', 
          'path/to/another.js': ['path/to/sources/*.coffee', 'path/to/more/*.coffee']
        }
      },
      glob_to_multiple: {
        expand: true,
        flatten: true,
        cwd: 'path/to',
        src: ['*.coffee'],
        dest: 'path/to/dest/',
        ext: '.js'
      }
    },
    compass: {
      dist: {
        sassDir: ['sass'],
        cssDir: ['css'],
        environment: 'production'
      },
      dev: {
        sassDir: ['sass'],
        cssDir: ['css'],
        environment: 'development'
      },
    },
    watch: {
      configFiles: {
        files: [ 'Gruntfile.js', 'config/*.js' ],
        options: {
          reload: true
        }
      },
      css: {
        files: ['sass/**/**/**'],
        tasks: ['compass:dev','shell:drushcssjs'],
        options: {
            spawn: false
        }
      },
      js: {
        files: ['coffee/**/**/**'],
        tasks: ['coffee:compileWithMaps','shell:drushcssjs'],
        options: {
            spawn: false
        }
      }
    }
  });
  grunt.loadNpmTasks('grunt-shell');
  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-contrib-compass');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.registerTask('dev', ['compass:dev','coffee:compileWithMaps','watch','shell:drushall']);
  grunt.registerTask('prod', ['compass:dist','coffee:compile','shell:drushall']);
  grunt.registerTask('default', ['dev']);
}

If you need some functionality, or you do not find what you are looking for, you have a great directory of plugins here.