Logo de Gulp

En esta ocasión tenemos el objetivo de agregar una nueva herramienta a tu toolbox de desarrollo web, una nueva arma en tu arsenal.

Quiero que aprendan Gulp. Con todo el cambio que Microsoft introdujo en los desarrolladores web con el anuncio de Asp.Net 5 y la gran cantidad de herramientas de otros stacks open source que se adicionaron a la plantilla de proyecto era inevitable no despertar la curiosidad por algunas cosas, así yo conocí Gulp.

Gulp, es un ejecutor de tareas que corre en node.js (task runner) que nos permite minificar, concatenar, compilar scripts y hojas de estilos (css, less, etc.), incluso minificar imágenes. La ventaja que tiene gulp es la sencillez con la que se realizan las tareas y el poco esfuerzo que supone configurar nuestro flujo de trabajo.

Gulp, utiliza node.js por lo que necesitamos tenerlo instalado. Si no lo tienes instalado aun puedes usar esta guía si tu maquina es windows, linux o mac.

Trabajando por Primera vez con Gulp

Para comenzar, creamos una carpeta para nuestra solución y dentro una para nuestro proyecto. Puedes hacerlo desde el explorar o desde la consola si gustas.

Crearemos la siguiente estructura dentro de nuestra carpeta del proyecto o puedes descargar los archivos desde aquí.

Estructura de carpetas

Para evitar conflictos con la ubicación de otros proyectos que utilizan paquetes instalados con npm, crearemos un archivo package.json en la carpeta del proyecto, en mi caso E:\Projects\FastGulp\FastGulp. Y añadimos el siguiente contenido:

{
  "version": "1.0.0",  
  "name": "FastGulp",  
  "private": true,  
  "devDependencies": {}
}

De esta manera nuestra estructura de carpetas quedaría así:
Estructura con package.json

Dentro de una consola, nos desplazamos hasta la ubicación del proyecto, en mi caso:

$ cd/d E:\Projects\FastGulp\FastGulp

consola

Luego a través de la consola y el comando npm install, instalamos gulp, primero globalmente para tener acceso a sus comandos en la consola y luego localmente para agregarlo a nuestro actual proyecto, el modificador --save-dev nos permite registrarlo en nuestro package.json como una dependencia.

$ npm install -g gulp
$ npm install gulp --save-dev

Gulp se enfoca en el uso de plugins así que instalaremos algunos para poder trabajar y mostrar el poder de gulp.

$ npm install gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-notify gulp-rename gulp-livereload gulp-cache del --save-dev

Y como pueden observar todas estas dependencias se registran en el *package.json y se descargan en carpetas dentro del folder node_modules, por si deseas curiosear.

package.json con dependencias

Trabajando en nuestro archivo gulpfile.js

Para poder poner gulp en acción necesitamos un archivo(.js) donde definir nuestras tareas. Al trabajar con gulp por convención llamamos al archivo gulpfile y lo colocamos en la raíz del proyecto.
ubicación del archivo gulp.js

1. Referenciando gulp y sus plugins

Gulp usa el enfoque de require para referenciar sus dependencias, así que debemos agregar estas líneas a nuestro gulp.js :

var gulp = require('gulp'),    
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),    
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    livereload = require('gulp-livereload'),
    del = require('del');

Parece un tanto tedioso tener que cargar los plugins de esta manera, pero quería asegurarme de mostrar la forma manual de hacerlo. Para más información sobre la carga de plugins.

2. Creando nuestra primera tarea

La api de gulp nos dice que para crear una tarea debemos seguir la siguiente sintaxis:

gulp.task('nombre-tarea', function() { ... )};

Así registramos la tarea con gulp, para que más adelante podamos ejecutar la tarea de la siguiente forma:

$ gulp nombre-tarea

Ahora en el cuerpo de la función que definimos al registrar la tarea usaremos el api de gulp y el procesamiento de algunos de los plugins que instalamos para, en este ejemplo, concatenar nuestros archivos css, agregar automáticamente prefijos de navegadores, minificarlos y colocarnos en una carpeta de destino.

En gulp, el enfoque es pensar en flujos(streams) y tuberías, los flujos representan los archivos que fluyen como agua y pasan a través de las tuberías(pipes) que representan los procesadores, como minificadores o autoprefixers.

Así que para este ejemplo haremos fluir los archivos css en la carpeta css en el folder src a través de preprocesadores.
Veamos como lo hacemos:

1-. Primero, obtenemos los archivos, para eso usamos la función src de gulp al cual le pasamos la ruta de un archivo o un conjunto de archivos usando el comodín (*). En este caso todos los archivos css en la carpeta css.

gulp.task('styles', function() {
  return gulp.src('src/css/*.css')
});

2.- Ahora que tenemos este stream de datos proveniente de varios archivos lo concatenaremos haciendo lo pasar a través del pipe concat. Concat permite asignarle un nombre al stream de manera que cuando lo guardemos tenga definido uno.

gulp.task('styles', function() {
  return gulp.src('src/css/*.css')
    .pipe(concat('styles.css'));
});

3.-Hagamos una prueba preliminar, guardando la unión de los archivos css, para eso usamos la api de gulp, a través de la función dest, la cual recibe como parámetro el folder donde se guardara el stream con el nombre dado por el pipe concat.

gulp.task('styles', function() {
  return gulp.src('src/css/*.css')
   .pipe(concat('styles.css'))
   .pipe(gulp.dest('dist/assets/css'))
});

Para ejecutar nuestra tarea, simplemente usamos la siguiente línea en una consola que este ubicada en la ruta de nuestro proyecto de tal manera que tenga acceso al archivo gulpfile.js.

$ gulp styles

ejecución de nuestra tarea

Y si observamos el contenido de la carpeta dist>assets>css podemos ver que nuestra primera y sobre simplificada tarea funciona bien.
resultado gulp styles

4.Ahora agreguemos un par de cosas para hacerlo más interesante. Usaremos al autoprefixer para agregar automáticamente prefijos específicos del navegador, el minifycss para minificar el css y dejarlo listo para producción y como un plus usaremos notify para escribir en la consola o en el caso de mi sistema operativo mostrar una notificación.

gulp.task('styles', function() {
  return gulp.src('src/css/*.css')
    .pipe(concat('styles.css'))
    .pipe(autoprefixer('> 5%'))
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(notify({ message: 'Css - tarea completada' }));
});

El autoprefixer agrega prefijos automáticamente cuando sea necesario, recibe un parámetro que especifica el nivel de compatibilidad, en este ejemplo, con todos los navegadores cuyo uso supere el 5%. Luego de agregar los prefijos, guardamos el resultado en dist/assets/css, luego continuamos el flujo y utilizamos rename para agregarle un sufijo a nuestro archivo, este stream que ahora tiene el nombre style.min.css pasa al minify para ser minificado, el resultado se guarda en la misma carpeta con el nuevo nombre. Y finalmente utilizamos notify para mostrar un mensaje.

5.-Veamos cómo marcha todo.
Ejecución de gulp styles
¿La notificación es genial verdad? Veamos los archivos que hemos generado.
Archivos generados

3. ¿Y qué tal con JavaScript?

Como pueden ver en el fragmento de código a continuación, minificar JavaScript es un proceso muy similar al que vimos con css, en el caso de JavaScript el pipe que minifica los archivos es uglify y bueno la única consideración en este caso es que al momento de unir los archivos deseo que jQuery si incluya primero, es por eso que utilicé una sobrecarga del método src que recibe un array de rutas, la primera específica y la segunda con un comodín.

gulp.task('scripts', function() {
  return gulp.src(
      [
          'src/js/jquery.js',
          'src/js/*.js'
      ])    
    .pipe(concat('scripts.js'))
    .pipe(gulp.dest('dist/assets/js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(gulp.dest('dist/assets/js'))
    .pipe(notify({ message: 'JS - tarea completada' }));
});

4. ¿Y ahora qué?

Antes de desplegar nuestro sitio y utilizar nuestros archivos minificados, es una buena idea limpiar los directorios donde colocamos nuestros archivos minificados antes de recompilarlos.
Así que veamos cómo crear una tarea que elimine los archivos de nuestros directorios:

gulp.task('clean', function(cb) {
    del(['dist/assets/css', 'dist/assets/js'], cb)
});

Nótese que utilizamos un parámetro cb para asegurarnos que la tarea asíncrona del termine antes de salir de la ejecución de la tarea. Para más información revisar la API de gulp.

5. Te presento a la tarea por defecto

Si recapitulamos un poco, recordaras que para ejecutar alguna de las tareas que creamos en nuestro gulpfile.js utilizamos un comando en nuestra consola en la siguiente forma:

$ gulp nombreTarea

Pero se han preguntado, ¿qué sucedería si no especifico un nombre? Pues les presento a la tarea por defecto, la tarea default. Si yo registro una tarea con el nombre default, esta se ejecutará cuando no se especifique el nombre de la tarea en nuestro comando gulp, y en unas líneas más abajo verán que es útil para agrupar todas nuestras tareas anteriormente definidas.

gulp.task('default', ['clean'], function() {
    gulp.start('styles', 'scripts');
});

6. Mmm... y ¿debo ejecutar estas tareas cada vez que cambie algo?

La respuesta inmediata es que si, cada vez que cambiamos nuestros archivos originales es necesario que volvamos a ejecutar nuestras tareas de gulp para volver a conseguir los resultados actualizados. Sin embargo, gulp nos permite registrar una o más tareas que ejecutar cuando los archivos de una o más rutas cambien. La sintaxis es simple, utilizamos la función watch de gulp para observar los cambios en los archivos que coincidan con
la ruta especificada, pueden observar el código a continuación.

gulp.task('watch', function() {
  // Watch archivos .css
  gulp.watch('src/css/*.css', ['styles']);
  
  // Watch archivos .js
  gulp.watch('src/js/*.js', ['scripts']);
});

Y con esto completamos nuestras herramientas basicas para poder empezar a usar gulp. Mi archivo gulp file, terminado para este ejemplo seria como sigue:

var gulp = require('gulp'),     
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),    
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    livereload = require('gulp-livereload'),
    del = require('del');

gulp.task('styles', function() {
  return gulp.src('src/css/*.css')
    .pipe(concat('styles.css'))
    .pipe(autoprefixer('> 5%'))
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(notify({ message: 'Css - tarea completada' }));
});

gulp.task('scripts', function() {
  return gulp.src(
      [
          'src/js/jquery.js',
          'src/js/*.js'
      ])    
    .pipe(concat('scripts.js'))
    .pipe(gulp.dest('dist/assets/js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(gulp.dest('dist/assets/js'))
    .pipe(notify({ message: 'JS - tarea completada' }));
});

// Clean
gulp.task('clean', function(cb) {
    del(['dist/assets/css', 'dist/assets/js'], cb)
});

gulp.task('default', ['clean'], function() {
    gulp.start('styles', 'scripts');
});

// Watch
gulp.task('watch', function() {

  gulp.watch('src/css/*.css', ['styles']);

  gulp.watch('src/js/*.js', ['scripts']);
});

Ejecución de las tareas en Gulp

7.Conclusiones

Como dice la canción, todo tiene su final, espero haber sido lo suficientemente claro, hay muchísimas más cosas que desearía contarles, pero ya habrá tiempo para ello en otros posts.

Hemos podido apreciar el poder y la simpleza de gulp para generar, registrar y ejecutar tareas, espero haberles mostrado su potencial. Intencionalmente no les he mostrado todo, no les mostré como usar pre compiladores de css, lint en JavaScript, pre compiladores de JavaScript ni minificadores de imágenes. Y lo hice porque espero que este post les despierte la curiosidad y lo busquen ustedes mismo. (De una u otra forma se los contare en el próximo post).

No se olviden que pueden revisar el código final en GitHub. Prueben la ejecución de las tareas ustedes mismos, no es lo mismo verlo en acción. Les agradezco el tiempo que invirtieron en leer este post y espero les haya sido útil. Pongan a prueba sus nuevos poderes ;)

power

YAPA: Pueden revisar también...