grunt

Utilitzar Grunt per automatitzar processos

Grunt és un task runner de JavaScript, una eina utilitzada per realitzar automàticament tasques freqüents com minificació, compilació, proves unitàries i linting.

Utilitza una interfície de línia d’ordres per executar tasques personalitzades definides en un arxiu (conegut com Gruntfile). Grunt es distribueix a través de NPM i compta amb multitud de complements disponibles.

Abans de començar, convé actualitzar NPM (Llibreria de programari, instal·lador i gestor de paquets), amb el comand:

npm update -g npm

Instal·lem el CLI (Command Line Interface) de grunt de forma global:

npm install -g grunt-cli

Ens desplacem a la carpeta del projecte (p.e. cd [nom-del-projecte]), des done preparem el projecte Grunt, creant el fitxer package.json, mitjançant la ordre:

npm init -y

Després instal·lem Grunt, generant les dependències:

npm install grunt --save-dev

En aquest punt, podem instal·lar els complements (tasques) que volem utilitzar en el nostre projecte, podem instal·lar les següents, dependent quines tasques volem utilitzar:

  • Eliminar fitxers i carpetes:
    npm install grunt-contrib-clean –save-dev
  • Concatenar fitxers:
    npm install grunt-contrib-concat –save-dev
  • Comprimir fitxers CSS:
    npm install grunt-contrib-cssmin –save-dev
  • Compilació fitxers Sass a CSS:
    npm install grunt-contrib-Sass –save-dev
  • Minificar fitxers JavaScript:
    npm install grunt-contrib-uglify –save-dev
  • Executar tasques predeterminades quan s’afegeixen, modifiquen o eliminen determinats fitxers:
    npm install grunt-contrib-watch –save-dev
  • Minificar imatges de format PNG, JPEG, GIF i SVG:
    npm install grunt-contrib-imagemin –save-dev
  • Validar fitxers amb JSHint:
    npm install grunt-contrib-jshint –save-dev
  • Copia fitxers:
    npm install grunt-contrib-copy –save-dev
  • Prefixar fitxers CSS:
    npm install grunt-autoprefixer –save-dev
  • Transferir fitxers usant FTP:
    npm install grunt-ftp-push –save-dev
  • Sincronitzar l’explorador:
    npm install grunt-browser-sync –save-dev

Ara queda definir en el fitxer Gruntfile.js les tasques concretes. En aquest fitxer tenim per una banda les tasques (grunt.initConfig), la càrrega dels complements (grunt.loadNPMTasks) i el registre de tasques (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']);

};

Per executar una tasca (p.e., distribute):

grunt distribute

En aquest cas, aquesta tasca realitza el següent:

  1. Elimina els fitxers de la carpeta dist.
  2. Autoprefija el fitxer CSS style.css de la carpeta src, generant el fitxer style.pre.css.
  3. Minimitza el fitxer style.pre.css, generant el fitxer style.css a la carpeta dist.
  4. Minimitza els arxius js de la carpeta src/js a la carpeta dist/js.
  5. Minimitza els arxius d’imatge de formats png, jpg, gif i svg de la carpeta src i les seves subcarpetes de la carpeta dist.
  6. Copia els fitxers de la carpeta dist a la carpeta del nostre projecte c:/xampp/htdocs/wordpress/wp-content/themes/projecte /.

Si executem grunt, es carreguen les tasques per defecte (default), que en aquest cas carrega el sincronitzador de l’explorador i l’observador (watch), que compila els fitxers Sass a CSS.

Si volem executar una tasca concreta, per exemple, pujar el nostre projecte local al nostre servidor web (p.e., ftp.proyecto.com), executem:

grunt pushweb

Si volem que la tasca de compilació es faci sempre que s’observen canvis en els fitxers Sass, executem la ordre:

grunt watch:sass

en el qual hem indicat la tasca i el seu target específic a executar.

Si volem buidar la carpeta dist-uploads:

grunt clean:uploads

Si volem minimitzar les imatges de la carpeta uploads i passar-les a la carpeta dist-uploads:

grunt imagemin:uploads

Modificant el fitxer Gruntfile.js podem generar noves tasques, redefinir i adaptar-les al nostre projecte en concret, de manera que automatitzar tasques repetitives que realitzem durant el desenvolupament del projecte, estalviant temps i millorant la productivitat.