Tutorial Less: 3. Compilar Less con Grunt

Logo gulpsimbolo masLogo less

¿Que es Grunt?

Grunt es un automatizador de tareas javaScript (concretamente desarrollado con Node.js) que nos permite realizar de forma automatizada todas las tareas repetitivas como minificar, compilar, validar, testear, comprimir, … lo que nos facilitara el trabajo al poder «olvidarnos» de todo este tipo de tareas que no aportan ningún valor para centrarnos en el trabajo real. La cantidad de plugins actualmente es de 6238 con lo que las posibilidades de que ya exista uno para las necesidades que busques son realmente altas.

En Grunt se hace la automatización mediante un sencillo archivo de configuración en el que hay que añadir la configuración de los plugins que se quieren utilizar para luego poder utilizarlos.

Instalar Grunt

Se instala mediante npm (el gestor de paquetes de Node.js) asi que el primer paso será instalarlo si no lo tenemos. Pues comprobar si lo tienes instalado o validar la instalación ejecutando los siguientes comandos.


node -v
v6.11.0

npm -v
3.10.10

Y con npm es tan sencillo instalar Grunt como ejecutar npm install -g grunt-cli y comprobamos que se ha instalado.


grunt -v
grunt-cli: The grunt command line interface (v1.2.0)

Fatal error: Unable to find local grunt.

If you're seeing this message, grunt hasn't been installed locally to
your project. For more information about installing and configuring grunt,
please see the Getting Started guide:

http://gruntjs.com/getting-started

Aunque veamos un error fatal no hay que asustarse porque se debe a que no tenemos ningún archivo de configuración de Grunt, pero para quedarnos más tranquilos podemos usar -version.


grunt -version
grunt-cli v1.2.0
grunt v1.0.1

Crear un proyecto con Grunt e instalar los plugins

El primer paso es crear un proyecto ejecutando npm init en el directorio del proyecto y vamos rellenando los campos (no es necesario rellenarlos todos) y confirmamos para que se genere el package.json que es archivo en el que se van a guardar los plugins que sean necesarios para ejecutar el proyecto para que lo podamos compartir y que cuando se descargue se puedan utilizar las versiones correctas de cada plugin usando simplemente npm install sobre el directorio del proyecto para que se instalen todas las dependencias.


npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (tutorial_less)
version: (1.0.0)
description: Tutorial de Less
entry point: (index.js)
test command:
git repository:
keywords:
author: Ivan Salas
license: (ISC)
About to write to D:\Programacion\programando\htdocs\tutorial_less\package.json:

{
  "name": "tutorial_less",
  "version": "1.0.0",
  "description": "Tutorial de Less",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Ivan Salas",
  "license": "ISC"
}


Is this ok? (yes) yes

Para instalar los plugins de Grunt que queramos utilizar ejecutamos npm install nombre_plugin –save-dev para cada uno de ellos.


npm install grunt --save-dev 
npm install grunt-contrib-less --save-dev 
npm install grunt-contrib-cssmin --save-dev 
npm install grunt-autoprefixer --save-dev 
npm install grunt-contrib-copy --save-dev
npm install grunt-contrib-watch --save-dev 
npm install matchdep --save-dev

Los plugins de Grunt comienzan con el prefijo grunt- o grunt-contrib- si son plugins oficiales, además de estos vamos a utilizar matchdep para cargar las dependencias en el archivo de configuración de Grunt de forma ‘automática’ para ahorrarnos tener que ponerlas todas.

Si volvemos a abrir el package.json ahora ya tendrá todas las dependencias por lo que ya lo tenemos listo para compartir.


{
  "name": "tutorial_less",
  "version": "1.0.0",
  "description": "Tutorial de Less",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Ivan Salas",
  "license": "ISC",
  "devDependencies": {
    "grunt": "^1.0.1",
    "grunt-autoprefixer": "^3.0.4",
    "grunt-contrib-copy": "^1.0.0",
    "grunt-contrib-cssmin": "^2.2.0",
    "grunt-contrib-less": "^1.4.1",
    "grunt-contrib-watch": "^1.0.0",
    "matchdep": "^1.0.1"
  }
}

El Gruntfile

El archivo de configuración de Grunt es el Gruntfile.js en el que se definen las configuraciones de cada una de los plugins y las tareas que vamos a poder realizar. Para trabajar con less el Gruntfile.js podría ser el siguiente.


module.exports = function(grunt) {

  // Carga todas las dependecias de grunt
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

  // Configuracion de grunt
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

        // Configuracion del plugin grunt-contrib-copy
        copy: {
            main: {
                expand: true,
                cwd: 'src/css',
                src: ["**/*.css"],
                dest: 'dist/css'
            }
        },

        // Configuracion del plugin grunt-contrib-cssmin
        cssmin: {
            target: {
                files: [{
                    expand: true,
                    cwd: 'src/css',
                    src: ['**/*.css', '!**/*.min.css'],
                    dest: 'dist/css',
                    ext: '.min.css'
                }]
            }
        },

        // Configuracion del plugin grunt-contrib-less
        less: {
            desarrollo: {
                options: {
                    paths: ['src/less']
                },
                files: [{
                  expand: true,
                  cwd: 'src/less',
                  src: ['**/*.less'], 
                  ext: '.css',
                  dest: 'src/css'
                }]
            },

            produccion: {
                options: {
                    paths: ['src/less'],
                    modifyVars: {
                        bgColor: 'green',
                        fontColor: 'blue'
                    }
                },
                files: [{
                  expand: true,
                  cwd: 'src/less',
                  src: ['**/*.less'], 
                  ext: '.css',
                  dest: 'src/css'
                }]
            }
        },

        // Configuracion del plugin grunt-autoprefixer
        autoprefixer: {
            dist: {
                files: [{
                    expand: true,
                    cwd: 'src/css',
                    src: ['**/*.css'],
                    dest: 'src/css',
                    ext: '.css'
                }]
            }
        },

        // Configuracion del plugin grunt-contrib-watch
        watch: {
            options: {
                nospawn: true,
                livereload: true
            },
            less: {
                files: ['src/less/**/*.less', 'src/css/**/*.css'],
                tasks: ['less:desarrollo', 'autoprefixer:dist']
            }
        }
    
  });

  // Tarea por defecto
  grunt.registerTask('default', ['watch']);

  grunt.registerTask('pro', ['less:produccion', 'autoprefixer:dist', 'cssmin', 'copy:main']);
};

Al ser básicamente configuración en su totalidad el Gruntfile es bastante simple en cuanto a su estructura y en lo referente a la configuración de las tareas suele ser también sencilla y en caso contrario o siempre podemos consultar la información del plugin/paquete en npmjs.

Lo primero es cargar las dependencias, para lo que utilizamos matchdep.


require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

También podríamos haberlas cargado del siguiente modo.


grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-autoprefixer');
//...

Y del principio al final del archivo donde indicamos las tareas que se van a poder ejecutar (grunt.registerTask(...);) que en este caso son 2, la tarea default que es la que se ejecutara cuando no se indique ninguna tarea de forma específica (cuando solo escribimos grunt en la consola) y una tarea específica que ejecuta secuencialmente las tareas de los módulos indicados en el segundo parámetro.


grunt.registerTask('default', ['watch']);
grunt.registerTask('pro', ['less:produccion', 'autoprefixer:dist', 'cssmin', 'copy:main']);

La configuración propia de las tareas no tiene mucho que contar porque nos hemos limitado básicamente a indicar los archivos de entrada y salida para cada tarea pero no quiero terminar sin comentar la posibilidad de añadir subtareas para nuestra tarea less lo que nos puede ser útil para tener variaciones para desarrollo y producción o para distintos entornos por ejemplo donde podremos aprovechar la opción modifyVars para redefinir valores a las variables de less que tengan valores diferentes según el entorno sin tener que editar los archivos .less con lo que podemos mantener la configuración base inalterada.

Y para terminar el proyecto de ejemplo de Grunt que hemos creado para descargar .

Leave a Reply