Sep 18, 2015

Grunt: development & distribution environments

Llevo trabajando con Grunt mucho tiempo, al principio solo lo utilizaba para compilar ficheros LESS/Sass o para concatenar y comprimir ficheros JS. Pero últimamente lo utilizo para crear entornos de trabajo diferenciados, por ejemplo un entorno para desarrollo y otro para distribución. Si quieres ver un ejemplo real, he creado una plantilla para un pet project.


Entorno de desarrollo

Es el entorno que utilizo para trabajar en local. Dentro del proyecto, se ubica en la carpeta dev. Este grupo de tareas se llama default y la registro de la siguiente forma:

	grunt.registerTask('default', ['connect:livereload', 'watch']);

Connect

Primero arranco un servidor Node con la ayuda del plugin Connect y abro automáticamente un navegador con la IP y el puerto especificado en la configuración. Por defecto http://localhost:9001.

Watch

Despúes configuro Watch conjuntamente con livereload. Esta combinación me permite observar modificaciones en un listado de ficheros y actualizar el navegador automáticamente cuando sea necesario. Esto mejora mucho mi flujo de trabajo, sobre todo si dispones de dos monitores. Es una maravilla ver los cambios aplicados inmediatamente en el navegador.

Sass

Utilizo el plugin Sass para Grunt para transformar -transpiling- mis ficheros scss a css. En el entorno dev, compilo Sass en modo expanded para poder debugear los ficheros css fácilmente.

Para que este plugin funcione necesitas tener instalado Ruby y Sass. Todas las configuraciones de salida de Sass las puedes encontrar en la documentación oficial.

JSHint

Por último utilizo JSHint para comprobar que mi código Javascript no contiene ningún error.

Este plugin se configura con un fichero llamado .jshintrc que en nuestro caso contiene los siguientes valores:

{
  // Require {} for every new block or scope
  "curly": true,

  // Tolerate use of `== null`
  "eqnull": true,

  // Require triple equals (===) for comparison
  "eqeqeq": true,

  // Require all non-global variables to be declared (prevents global leaks)
  "undef": true,

  // Custom globals: additional predefined global variables
  "globals": {
    "jQuery": true,
    "createjs": true,
    "document": true,
    "require": true,
    "module": true
  }
}

Si estas opciones se te quedan cortas, existen multiples formas de configurar JSHint.


Entorno de distribución

Es el entorno que utilizo para hacer deploy en el servidor de producción. Dentro del proyecto, se ubica en las carpetas dist y tmp. Ambas carpetas se encuentran añadidas al .gitignore, ya que no nos interesa tenerlas en el repositorio de versiones.

Este grupo de tareas se llama dist y la registro de la siguiente forma:

grunt.registerTask('dist', ['clean', 'copy', 'uglify', 'processhtml', 'htmlmin:dist', 'sass:dist', 'cachebreaker']);

La configuración de este entorno es un poco más compleja, ya que requiere un mayor número de plugins, todos ellos debidamente coordinados. El orden de las tareas es importante. Tal como funciona Grunt, necesito modificar y guardar ficheros en la carpeta temp para que todas las tareas puedan hacer su trabajo.

Clean

Primero utilizo el plugin Clean para borrar todo el contenido de las carpetas dist y tmp. Esto me facilita tener en todo momento un entorno de distribución actualizado, sin archivos innecesarios.

Copy

El plugin Copy me permite copiar ficheros de una carpeta a otra. En este caso desde dev/assets a dist/assets. En esta carpeta guardo todos los assets que necesita mi proyecto: css, js, imagenes, fuentes, etc.

Uglify

Uglify me permite concatenar y comprimir los ficheros .js de la aplicación.

Process HTML

Process html es un plugin muy útil para editar o eliminar partes de un fichero HTML. Imaginate que tu aplicación trabaja con 5 librerías Javascript distintas. En modo desarrollo te interesa tenerlas separadas para debugear tranquilamente, pero cuando vas a hacer un deploy necesitas tenerlas todas en un solo fichero.

Con este plugin podrás especificar exactamente este comportamiento. Por ejemplo estas dos librerías:

<!-- build:js app.js -->
<script src="my/lib/path/lib.js"></script>
<script src="my/deep/development/path/script.js"></script>
<!-- /build -->

Se convertirán en esta otra:

<script src="app.js"></script>

Incluso puedes eliminar una etiqueta o grupo de etiquetas del fichero HTML

<!-- build:remove -->
<span id="debug">Soy un texto para el entorno de desarrollo</span>
<!-- /build -->

Y la configuración es bastante simple:

HTML Min

HTML Min hace exactamente lo que su nombre indica, comprime el fichero HTML. Incluso puedes especificar que elimine los comentarios que pueda tener tu fichero.

Sass

En el entorno dist, compilo Sass en modo compressed. Para que este plugin funcione necesitas tener instalado Ruby y Sass. Todas las configuraciones de salida de Sass las puedes encontrar en la documentación oficial.

Cache Breaker

Cache Breaker me permite engañar al browser añadiendo un timestamp a las urls que yo decida.


Utilidades

Además la plantilla dispone de un par de plugins extras.

FTP Deploy

Con FTP Deploy puedo realizar subidas por FTP automáticamente. Esta tarea la tengo registrada como ftp y va unida con la tarea de distribución.

grunt.registerTask('ftp', ['dist', 'ftp-deploy']);

En la configuración, además de los datos obvios (host, puerto, carpeta de destino, etc.), debes especificar la key del fichero .ftppass que contiene tu nombre de usuario y contraseña.

Bowercopy

Dependiendo del autor de la librería encontrarás muchas carperas y ficheros que no serán necesarios para tu proyecto. Hago uso de Bowercopy para copiar únicamente los ficheros que necesita mi proyecto.