CategoríaTecnología e Informática

Automatizando temas web con grunt

Cuando desarrollas temas para webs, además del trabajo de crearlas, necesitamos crear nuestros archivos definitivos para distribuirlos. Esto implica copiar archivos, unirlos, minimizarlos… Una tarea perfecta para grunt.

En este artículo quiero comentar mi configuración de grunt mediante el archivo Gruntfile.js con los paquetes que uso habitualmente, teniendo en cuenta que utilizamos como gestor de paquetes npm, que tenemos nuestros archivos fuente en la carpeta src y que la compilación final para producción debe acabar en la carpeta dist.

Copiar archivos

Una (muy) buena práctica mientras desarrollamos nuestro producto consiste en mantener separados los archivos fuente, en los que realizamos nuestras modificaciones, de los archivos finales compilados, de forma que podemos eliminar la última compilación sin riesgo de eliminar nuestros archivos originales por error.

Lo primero que necesitaremos será instalar las dependencias de la última versión de grunt y grunt-contrib-copy a través de npm como es habitual.

npm install grunt grunt-contrib-copy --save

Luego creamos el archivo Gruntfile.js y cremos nuestra primera tarea:

module.exports = function(grunt) {

  // configura las tareas
  grunt.initConfig({

    copy: {
      build: {
        cwd: 'src',
        src: [ '**' ],
        dest: 'dist',
        expand: true
      },
    },

  });

  // carga las tareas
  grunt.loadNpmTasks('grunt-contrib-copy');

  // define los comandos para las tareas
};

Lo que estamos haciendo es crear una tarea que nos permite copiar los archivos fuente de nuestro proyecto, que tenemos en la carpeta src hasta la carpeta de distribución dist.

Limpiar directorio destino

Para no ir arrastrando los archivos copiados en la primera fase que vayan desapareciendo, necesitaremos poder limpiar dicho directorio. Para ello necesitamos instalar y configurar el paquete grunt-clean.

npm install grunt-clean --save

agregamos la configuración de la tarea, especificando el directorio a limpiar antes de la nueva compilación incluyendo las siguientes líneas en el archivo Gruntfile.js bajo la tarea anterior:

    clean: {
      build: {
        src: [ 'dist' ]
      },
    },

Tarea que cargamos agregando la línea

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

Preparamos el comando build para usarlo desde la línea de comandos con grunt de forma que primero vacíe el directorio dist y luego copie nuestros archivos fuente. Para ello, agregamos al final del archivo Gruntfile.js la siguiente línea.

grunt.registerTask(
  'build', 
  'Copia todos los archivos fuente al directorio de distribución.', 
  [ 'clean', 'copy' ]
);

Servidor local de prueba

Una vez copiados los archivos, necesitamos un servidor de prueba que nos permita ver nuestra creación. Para ello utilizaremos el paquete grunt-contrib-connect:

npm install grunt-contrib-connect --save

Lo configuramos para que sirva nuestros archivos del directorio dist en el puerto 3000, utilizando como nombre del host * que nos permite acceder desde cualquier ubicación.

    connect: {
      server: {
        options: {
          port: 3000,
          base: 'dist',
          hostname: '*'
        }
      }
    }

Agregamos la carga de la tarea mediante la siguiente línea:

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

Si intentamos ejecutar la tarea connect vemos que el servidor arrana e inmediatamente se vuelve a parar. Esto es porque la tarea connect no se ejecuta indefinidamente. Para solucionar esto aprovecharemos el funcionamiento del paquete grunt-contrib-watch con un doble propósito: por un lado mantendré el servidor funcionando, pero, por otro, también estará vigilando los cambios en nuestros archivos fuente para reflejar los cambios en el servidor activo.

Observando los cambios

Instalamos el paquete grunt-contrib-watch

npm install grunt-contrib-watch --save

Y la configuramos para que lance la tarea de copiar los archivos fuente al directorio de distribución cada vez que se modifiquen:

    watch: {
      copy: {
        files: [ 'src/**' ],
        tasks: [ 'copy' ]
      }
    },

Cargamos la tarea con

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

Ahora creamos una tarea por defecto que se encargue de llamar a la tarea de compilación, lance el servidor local de prueba y se quede vigilando los cambios para nuestro desarrollo continuo.

  grunt.registerTask(
    'default',
    'Vigila los cambios en el proyecto y automáticamente copia los archivos y lanza el servidor de prueba.',
    [ 'clean', 'copy', 'connect', 'watch' ]
  );

Ahora si desde la consola usamos el comando grunt se compilará nuestro proyecto, se lanzará el servidor y podremos ir modificando archivos y viéndolos en nuestro servidor local.

Compilando los estilos

Para desarrollar nuestras hojas de estilo resulta muy útil utilizar un preprocesador como stylus, sass o less. Todos ellos tienen un paquete para usarlo con grunt. La configuración es similar, así que, en este caso mostraré como utilizar less.

Como siempre, lo primero será instalar el paquete necesario:

npm install grunt-contrib-less --save

Configuramos la tarea, para utilizarla de forma diferente mientras estamos en fase de desarrollo continuo o en producción:

    less: {
      dev: {
        files: {
          "dist/css/styles.css": "src/css/styles.less"
        }
      },
      prod: {
        options: {
        },
        files: {
          "dist/css/styles.css": "src/css/styles.less"
        }
      },
    }

Y, por supuesto, no debemos olvidar cargar la tarea:

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

Registramos una nueva tarea stylesheets y la agregamos a la tarea build la tarea dentro de la configuración del comando build en el caso de producción y a la tarea por defecto para el desarrollo continuado:

  grunt.registerTask(  
    'build', 
    'Copia todos los archivos fuente al directorio de distribución y compila las hojas de estilo con less.', 
    [ 'clean', 'copy', 'stylesheets' ]
  );

  grunt.registerTask(
    'stylesheets', 
    'Compila las hojas de estilo.', 
    [ 'less:prod' ]
  );

  grunt.registerTask(
    'default',
    'Vigila los cambios en el proyecto y automáticamente copia los archivos y lanza el servidor de prueba.',
    [ 'clean', 'copy', 'less:dev', 'connect', 'watch' ]
  );

Como ya estamos compilando los archivos less en archivos css, no tiene ningún sentido copiar estos archivos al directorio de distribución, así que modificamos la configuración de la tarea de copia para excluirlos:

    copy: {
      build: {
        cwd: 'src',
        src: [ '**', '!**/*.less' ],
        dest: 'dist',
        expand: true
      },
    },

Para no tener que tener en cuenta los diferentes navegadores, podemos usar el plugin autoprefix que lo hará por nosotros, pero tendremos que configurarlo como una dependencia de less.

Primero instalamos la dependencia:

npm install less-plugin-autoprefix --save

Y configuramos la tarea less agregándolo para la compilación en producción:

      prod: {
        options: {
          plugins: [
            new (require('less-plugin-autoprefix'))({browsers: ["last 2 versions"]}),
          ]
        },
        files: {
          "dist/css/styles.css": "src/css/styles.less"
        }
      }

Por último, comprimimos los archivos de estilo generados por less mediante el plugin clean-css

npm install less-plugin-clean-css --save
      prod: {
        options: {
          plugins: [
            new (require('less-plugin-autoprefix'))({browsers: ["last 2 versions"]}),
            new (require('less-plugin-clean-css'))({advanced:true})
          ]
        },
        files: {
          "dist/css/styles.css": "src/css/styles.less"
        }
      }

Unión de css

Desde el punto de vista de la optimización de la carga de una página web, resulta una buena práctica unir ficheros css en uno solo, que, aunque sea de mayor tamaño, reduce el número de peticiones al servidor web, mejorando la velocidad de carga.

Utilizaremos el paquete grunt-contrib-cssmin para realizar esta tarea. De nuevo, lo primero será instalarlo:

npm install grunt-contrib-cssmin --save

La configuración de la tarea debe contener el origen y el destino de los css:

    cssmin: {
      build: {
        files: {
          'dist/css/application.css': [ 'dist/**/*.css' ]
        }
      }
    },

Agregamos esta tarea en el registro de la tarea stylesheets para que sea utilizada por el comando build:

  grunt.registerTask(
    'stylesheets', 
    'Compila las hojas de estilo.', 
    [ 'less:prod', 'cssmin' ]
  );

En este punto, si ejecutamos el comando grunt build tendremos los archivos css antes de unirlos y el resultado final de la unión. ¿Cómo arreglamos esto? Agregando una subtarea a la tarea clean

    clean: {
      build: {
        src: [ 'dist' ]
      },
      stylesheets: {
        src: [ 'dist/**/*.css', '!dist/css/application.css' ]
      },
    },

Ahora debemos agregar esta subtarea en el registro de la tarea stylesheets.

  grunt.registerTask(
    'stylesheets', 
    'Compila las hojas de estilo.', 
    [ 'less:prod', 'cssmin', 'clean:stylesheets' ]
  );

Tratando los scripts

Como preprocesador asumiré que los creamos con coffescript y después los compilamos a javascript. También los uniremos y comprimiremos. Para todo esto, instalamos los siguientes paquetes:

npm install grunt-contrib-jshint grunt-contrib-coffee grunt-contrib-uglify

Y, de nuevo, cargamos las tareas:

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

Lo primero será configurar la tarea coffescript, paso que ignoramos en caso de usar JavaScript directamente.

    coffee: {
      build: {
        expand: true,
        cwd: 'src',
        src: [ '**/*.coffee' ],
        dest: 'dist',
        ext: '.js'
      }
    },

Esta tarea convierte nuestros archivos coffeescript en JavaScript en el directorio de destino.

Registramos una nueva tarea que debemos incluir en la tarea por defecto y en la tarea build.

  grunt.registerTask(
    'scripts', 
    'Compila los archivos JavaScript.', 
    [ 'coffee' ]
  );

  grunt.registerTask(  
    'build', 
    'Copia todos los archivos fuente al directorio de distribución, compila las hojas de estilo con less y los coffeescript a javascript.', 
    [ 'clean', 'copy', 'stylesheets', 'scripts' ]
  );

No queremos que nuestros archivos de coffeescript se copien al directorio de la versión final, así que agregamos una excepción:

    copy: {
      build: {
        cwd: 'src',
        src: [ '**', '!**/*.less', '!**/*.coffee' ],
        dest: 'dist',
        expand: true
      },
    },

Una vez compilados, los unimos en un solo archivo javascript para reducir los tiempos de carga mediante la tarea uglify.

    uglify: {
      build: {
        options: {
          mangle: false
        },
        files: {
          'dist/js/application.js': [ 'src/**/*.js' ]
        }
      }
    },

Agregamos la nueva tarea a la tarea registrada para el tratamiento de scripts:

  grunt.registerTask(
    'scripts', 
    'Compila los archivos JavaScript.', 
    [ 'coffee', 'uglify' ]
  );

De nuevo es necesario eliminar los archivos originales que había antes de unirlos agregando una subtarea a la tarea clean:

    clean: {
      build: {
        src: [ 'dist' ]
      },
      stylesheets: {
        src: [ 'dist/**/*.css', '!dist/css/application.css' ]
      },
      scripts: {
        src: [ 'dist/**/*.js', '!dist/application.js' ]
      },
    },

Que tendremos que agregar al tratamiento de scripts:

  grunt.registerTask(
    'scripts', 
    'Compila los archivos JavaScript.', 
    [ 'coffee', 'uglify', 'clean:scripts' ]
  );

El archivo completo quedará como sigue:

https://gist.github.com/tonybolanyo/26d6f7d65b7d5ad4d3ab.js

Con esto lo tenemos todo listo para comenzar nuestro proyecto de diseño de un tema web. ¿Por dónde empezar? HTML5Boilerplate, pero eso será en otro artículo.

ebooks: La colección «Succintly»

Siempre estoy buscando nuevos recursos para avanzar en las múltiples tecnologías que cualquier programador o desarrollador necesita. En esta ocasión os quería recomendar una amplia colección de libros básicos que abarcan multitud de esas tecnologías, desde lenguajes de programación a control de versiones pasando por automatización de tests de nuestros programas.

La colección tiene actualmente 80 libros a nuestra disposición que podemos descargar gratuitamente desde la web de Syncfusion simplemente creando una cuenta gratuita.

Se trata de la colección «Succintly» (sucintamente) que, tal y como sugiere el nombre, nos habla brevemente (en 100 páginas) sobre un tema o tecnología concreta.

Algunos de los temas tratados son, por ejemplo, apache, c#, desarrollo ágil, LINQ o, incluso arduino. Todos los libros están en inglés y podemos descargarlos en dos formatos diferentes, mobi, adecuado para los lectores de libros electrónicos como Kindle o, en PDF, si preferimos una edición para leer en nuestro PC.

Podéis encontrar la colección de libros en el siguiente enlace:

https://www.syncfusion.com/resources/techportal/ebooks

A %d blogueros les gusta esto: