grunt

Use Grunt to automate processes

Grunt is a JavaScript task runner, a tool used to automatically perform frequent tasks like minification, compilation, unit tests and linting.

It uses a command line interface to execute custom tasks defined in a file (known as Gruntfile). Grunt is distributed through npm and has a multitude of add-ons available.

Before you start, you should update NPM (Software Library, Installer and Package Manager), with the command:

npm update -g npm

We install the grunt CLI (Command Line Interface) globally:

npm install -g grunt-cli

We move to the project folder (e.g. cd [project-name]), from where we prepare the Grunt project, creating the package.json file, using the command:

npm init -y

Then we install Grunt, generating the dependencies:

npm install grunt --save-dev

At this point, we can install the add-ons (tasks) that we want to use in our project, we can install the following, depending on what tasks we want to use:

  • Delete files and folders:
    npm install grunt-contrib-clean –save-dev
  • Concatenate files:
    npm install grunt-contrib-concat –save-dev
  • Compress CSS files:
    npm install grunt-contrib-cssmin –save-dev
  • Compile Sass files to CSS:
    npm install grunt-contrib-sass –save-dev
  • Minify JavaScript files:
    npm install grunt-contrib-uglify –save-dev
  • Execute default tasks when certain files are added, modified or deleted:
    npm install grunt-contrib-watch –save-dev
  • Minify images of PNG, JPEG, GIF and SVG format:
    npm install grunt-contrib-imagemin –save-dev
  • Validate files with JSHint:
    npm install grunt-contrib-jshint –save-dev
  • Copy files:
    npm install grunt-contrib-copy –save-dev
  • Prefix CSS files:
    npm install grunt-autoprefixer –save-dev
  • Transfer files using FTP:
    npm install grunt-ftp-push –save-dev
  • Synchronize the browser:
    npm install grunt-browser-sync –save-dev

Now it remains to define in the Gruntfile.js file the specific tasks. In this file we have on the one hand the tasks (grunt.initConfig), the loading of the complements (grunt.loadNPMTasks) and the task log (grunt.registerTask).

module.exports = function(grunt) {
  grunt.initConfig({
    sass: {
      dev: {
        files: {
          'src/style.css': 'src/sass/style.scss'
        }
      },
      dist: {
        options: {
          style: 'compressed',
          sourcemap: 'none'
        },
        files: [{
          expand: true,
          cwd: 'src/sass',
          src: ['.scss'],
          dest: 'src',
          ext: '.css'
        }]
      }
    },
    autoprefixer: {
      options: {
        browsers: ['last 2 version','ie 10'],
        grid: true
      },
      dist: {
        files: [{
            expand: true,
            cwd: 'src',
            src: 'style.css',
            dest: 'src',
            ext: '.pre.css'
        }]
      }
    },
    cssmin: {
      dist: {
        src: 'src/style.pre.css',
        dest: 'dist/style.css'
      }
    },
    clean: {
      dist: {
        src: ['dist/']
      },
      uploads: {
        src: ['dist-uploads/']
      }
    },
    concat: {
      options: {
        separator: ';',
      },
      js: {
        src: ['src/js/.js','!src/js/built.js'],
        dest: 'src/js/built.js'
      }
    },
    uglify: {
      dist: {
        files: [{
          expand: true,
          cwd: 'src/',
          src: 'js/.js',
          dest: 'dist/',
        }]
      }
    },
    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: 'src/',
          src: ['/.{png,jpg,gif,svg}'],
          dest: 'dist/'
        }]
      },
      uploads: {
        files: [{
          expand: true,
          cwd: 'uploads/',
          src: ['/.{png,jpg,gif,svg}'],
          dest: 'dist-uploads/'
        }]
      }
    },
    copy: {
      dist: {
        expand: true,
        cwd: 'src/',
        src: ['/.php'],
        dest: 'dist/'
      },
      style: {
        expand: true,
        cwd: 'src/',
        src: ['style.css'],
        dest: 'dist/'
      },
      localhost: {
        expand: true,
        cwd: 'dist/',
        src: ['/'],
        dest: 'c:/xampp/htdocs/wordpress/wp-content/themes/proyecto/'
      }
    },
    ftp_push: {
      options: {
        host: 'ftp.proyecto.com',
        username: 'user',
        password: 'pass',
        dest: 'proyecto',
      },
      dist: {
        files: [{
          expand: true,
          cwd: 'dist/',
          src: ['/'],
          dest:'wp-content/themes/proyecto/'
        }]
      },
      uploads: {
        files: [{
          expand: true,
          cwd: 'dist-uploads/',
          src: ['/'],
          dest:'wp-content/uploads/'
        }]
      }
    },
    browserSync: {
      dev: {
          bsFiles: {
            src : ['c:/xampp/htdocs/wordpress/wp-content/themes/proyecto//']
          },
          options: {
            watchTask: true,
            proxy: 'localhost/proyecto',
          }
      }
    },
    jshint: {
      dev: ['src/js/.js']
    },
    watch: {
      options: {
        livereload: true
      },
      sass: {
        files: ['src/sass/style.scss'],
        tasks: ['sass:dev']
      },
      diststyle: {
        files: ['src/style.css'],
        tasks: ['clean:dist','copy:style','copy:localhost']
      },
      js: {
        files: ['src/js/.js'],
        tasks: ['jshint:dev']
      }
    },
  });

  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-imagemin');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-browser-sync');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-ftp-push');
  grunt.loadNpmTasks('grunt-autoprefixer');
  
  grunt.registerTask('default',['browserSync:dev','watch:sass','watch:diststyle']);
  
  grunt.registerTask('distribute',[
    'clean:dist',
    'autoprefixer:dist',
    'cssmin:dist',
    'uglify:dist',
    'imagemin:dist',
    'copy:dist'
    ]);

  grunt.registerTask('pushweb',['ftp_push:dist']);

  grunt.registerTask('pushuploads',['clean:uploads','imagemin:uploads','ftp_push:uploads']);

  grunt.registerTask('pushlocal', ['copy:localhost']);

};

To execute a task (e.g., distribute):

grunt distribute

In this case, this task performs the following:

  1. Delete the files from the dist folder.
  2. Autofix the CSS style.css file in the src folder, generating the style.pre.css file.
  3. Minimize the style.pre.css file, generating the style.css file in the dist folder.
  4. Minimize the js files in the src/js folder to the dist/js folder.
  5. Minimize image files of png, jpg, gif and svg formats from the src folder and their subfolders to the dist folder.
  6. Copy the files from the dist folder to the folder of our project c: / xampp / htdocs / wordpress / wp-content / themes / project /.

If we execute grunt, the default tasks will be loaded, which in this case loads the browser synchronizer and the watch, which compiles the Sass files to CSS.

If we want to execute a specific task, for example, upload our local project to our web server (e.g., ftp.proyecto.com), we execute:

grunt pushweb

If we want the compilation task to be carried out whenever changes are observed in the Sass files, we execute the command:

grunt watch:sass

in which we have indicated the task and its specific target to execute.

If we want to empty the dist-uploads folder:

grunt clean:uploads

If we want to minimize the images in the uploads folder and pass them to the dist-uploads folder:

grunt imagemin:uploads

By modifying the Gruntfile.js file we can generate new tasks, redefine them and adapt them to our specific project, thereby automating repetitive tasks that we perform during the development of the project, saving time and improving productivity.