Sistemas de automatización
Porque hoy en día el flujo de trabajo de un desarrollador se ha vuelto más complejo, usamos muchas herramientas de desarrollo, por lo cual configurar cada tarea y ejecutarla “manualmente” y por separado requiere demasiado tiempo, para soluciónar este problama tenemos sistemas de automatización de tareas.
Gulp.js
Gulp.js es un build system(sistema de construcción) que permite automatizar tareas comunes de desarrollo, tales como la minificación de código JavaScript, recarga del navegador, compresión de imágenes, validación de sintaxis de código y un sin fin de tareas más.
¿Cómo funciona Gulp.js?
Gulp.js utiliza el módulo Stream de Node.js, lo cual lo hace más rápido para construir, a diferencia de Grunt.js.
Gulp.js no necesita escribir archivos y/o carpetas temporales en el disco duro, lo cual supone que realizará las mismas tareas que Grunt.js pero en menor tiempo.
Gulp.js utiliza el método pipe()
, este método obtiene todos los datos de un Stream legible(readable) y lo escribe en destino que le indiquemos ó de lo contrario lo entrega o sirve hacia otro pipe.
En la siguiente imagen veremos como Grunt.js manipula los archivos al realizar sus tareas:
Y en esta veremos como Gulp.js manipula los archivos al realizar sus tareas:
Como podemos ver, aunque los 2 hicieron la misma tarea Gulp.js no escribió archivos temporales en el disco duro. Gulp.js realizó 4 operaciones y en cambio Grunt.js realizó 8 operaciones.
Gulp.js utiliza el poder del paquete Node.js vinyl-fs para leer y escribir Streams.
Gulp.js también utiliza un paquete Node.js para la secuenciación, ejecución de tareas y dependencias en máxima concurrencia, llamado Orchestrator.
Más adelante veremos con mayor detalle como trabajan los Streams de Node.js.
¿Cómo instalar Gulp.js?
Para instalarl gulp.js de modo global
npm install -g gulp
Si estás usando Linux o Mac, tal vez necesites anteponer la palabra sudo para poder ejecutar este comando con los permisos de administrador, así:
sudo npm install -g gulp
Verificamos que Gulp.js ha sido instalado correctamente.
gulp -v
Si lo tenemos instalado correctamente, nos mostrará lo siguiente:
CLI version 3.9.1
¿Cómo usar Gulp.js?
Una vez instalado en nuestro sistema estamos listos para crear nuestro primer proyecto usando Gulp.js, nuestro pequeño proyecto concatenará dos archivos .js en uno solo y luego lo minificará. Así que configuraremos 2 tareas(concatenar y minificar), todo esto contenido en una tarea llamada “demo”.
Creamos una carpeta llamada: gulp-primeros-pasos, ingresamos a esa carpeta mediante terminal.
Luego allí dentro creamos nuestro archivo: gulpfile.js, que es el archivo que Gulp.js necesita para saber que tareas realizará y de momento no le podremos ningún contenido.
Luego escribimos lo siguiente(en este punto suponemos que tenemos instalado Node.js)
npm init
Npm nos pedirá los datos de nuestro proyecto, ya que en esta ocasión sólo estamos haciendo un demo. Simplemente presionaremos Enter a todas las preguntas.
Con esto, Npm nos debe haber creado un archivo llamado: package.json, que contendrá algo parecido a lo siguiente:
{
"name": "gulp-primeros-pasos",
"version": "0.0.1",
"description": "Gulp: Primeros pasos",
"main": "gulpfile.js",
"author": "jansanchez",
"license": "MIT"
}
Ahora agregaremos las dependencias de desarrollo a nuestro proyecto, la primera a instalar será: gulp, así que escribimos lo siguiente en nuestra terminal:
npm install --save-dev gulp
Luego instalamos: gulp-concat
npm install --save-dev gulp-uglify
Tengamos en cuenta que sí no agregamos el parámetro: –save-dev, entonces Npm no agregará este paquete como una dependencia de desarrollo de nuestro proyecto y mucho menos lo agregará a nuestro archivo package.json.
Como podremos observar, nuestro archivo package.json a cambiado y debería contener algo parecido a lo siguiente:
{
"name": "gulp-primeros-pasos",
"version": "0.0.1",
"description": "Gulp: Primeros pasos",
"main": "gulpfile.js",
"author": "jansanchez",
"license": "MIT",
"devDependencies": {
"gulp": "^3.8.7",
"gulp-concat": "^2.3.4",
"gulp-uglify": "^0.3.1"
}
}
Como vemos, se agregó la clave devDependencies y en su interior se comienzan a guardar nuestras dependencias de desarrollo y la versión que hemos instalado localmente.
Luego vamos a crear las siguientes carpetas y archivos:
Creamos la carpeta js y dentro de esta carpeta crearemos la carpeta source.
Dentro de la carpeta source crearemos el archivo 1.ts y le agregaremos el siguiente contenido:
// contenido del archivo 1.js
var sumar = function (a, b){
return a + b;
};
Nuevamente dentro de la carpeta source crearemos el archivo 2.js y le agregaremos el siguiente contenido:
// contenido del archivo 2.js
var restar = function (a, b){
return a - b;
};
Ahora vamos a poner el siguiente contenido a nuestro archivo gulpfile.js.
/*
* Dependencias
*/
var gulp = require('gulp'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify');
/*
* Configuración de la tarea 'demo'
*/
gulp.task('demo', function () {
gulp.src('js/source/*.js')
.pipe(concat('todo.js'))
.pipe(uglify())
.pipe(gulp.dest('js/build/'))
});
Con esto ya tenemos todo configurado, así que para ponerlo a prueba en nuestra terminal escribimos lo siguiente:
gulp demo
Y si todo anda bien, nos dará el siguiente mensaje:
[11:23:09] Using gulpfile ~/htdocs/gulp-primeros-pasos/gulpfile.js
[11:23:09] Starting 'demo'...
[11:23:09] Finished 'demo' after 9 ms
El cual nos indica que la tarea demo se ejecutó con éxito en 9 milisegundos.
Para comprobar si se ejecutaron las 2 tareas requeridas, nos dirigimos a la carpeta js/build y abrimos el archivo todo.js y nos debe mostrar el siguiente contenido:
var sumar=function(r,n){return r+n},restar=function(r,n){return r-n};
Como vemos, con unas simples y limpias lineas de código hemos realizado 2 tareas de desarrollo comunes(concatenar y minificar archivos .js).
Analizando el gulpfile.js
Ahora vamos a analizar el código que escribimos en nuestro gulpfile.js para entender un poco más como funciona Gulp.js.
Primero para llevar a cabo las tareas que deseamos, requerimos los siguientes paquetes: gulp, gulp-concat y gulp-uglify, así:
/*
* Dependencias
*/
var gulp = require('gulp'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify');
API - Documentación
Gulp.js tiene una pequeña API, esto te permitirá aprender Gulp.js rápidamente.
GULP.TASK()
Con el método gulp.task() definimos una tarea, este método toma 3 argumentos: el nombre de la tarea, la ó las tareas de las que depende esta tarea y la función que ejecutará al llamar esta tarea.
En nuestro ejemplo sólo usamos 2 parámetros: el nombre y la función, así:
/*
* Configuración de la tarea 'demo'
*/
gulp.task('demo', function () {
// Contenido de la tarea 'demo'
});
Con lo cual declaramos “demo” como nombre de la tarea y dentro escribimos lo que deseamos que haga nuestra tarea.
Así que si queremos llamar esta tarea tan solo escribimos en nuestra terminal:
gulp demo
Lista de tareas
Una tarea también puede actuar como una lista de tareas, supongamos que queremos definir una tarea que corra otras 3 tareas por ejemplo: imágenes, css y js. Entonces escribiríamos lo siguiente:
gulp.task('estaticos', ['imagenes', 'css', 'js']);
Lo que quiere decir que al ejecutar la tarea “estaticos” con el comando gulp estaticos se ejecutarán estas 3 tareas.
El detalle es que estas tareas correran asíncronamente, osea que correrán todas juntas al mismo tiempo sin ningún orden de ejecución.
Tareas como dependencia
Si deseamos que una tarea se ejecute sí y solo sí otra tarea haya terminado antes, entonces podemos hacer lo siguiente:
gulp.task('css', ['imagenes'], function () {
/*
* Aquí iría el contenido de mi tarea 'css'
* Que se ejecutará solo cuando la tarea
* 'imagenes' haya terminado.
*/
});
Entonces, cuando corramos la tarea “css”, Gulp.js ejecutará primero la tarea “imagenes”, esperará a que esta tarea termine y luego recién ejecutará la tarea “css”.
TAREA POR DEFECTO(DEFAULT)
Gulp.js nos permite definir una tarea por defecto, que corra tan solo al escribir el comando gulp. Esto se puede hacer tan solo poniendole a la tarea el nombre de default, así:
gulp.task('default', function () {
/*
* Código de nuestra tarea por defecto.
*/
});
Y claro, también puedes hacer que tu tarea por defecto sea una lista de tareas, así:
gulp.task('default', ['css', 'js']);
Esta tarea ejecutará las tarea ‘css’ y ‘js’, tan solo escribiendo en nuestra terminal:
gulp
gulp.src()
El método gulp.src() toma como parámetro un valor glob es decir, una cadena que coincida con uno o más archivos usando los patrones que usa el intérprete de comandos de unix(shell) y retorna un stream que puede ser “pipeado” a un plugin adicional ó hacia el método gulp.dest().
Este parámetro puede ser una cadena o una colección(Array) de valores glob.
Gulp.js usa el paquete de Node.js node-glob para obtener los archivos especificados en él ó los globs ingresados.
Ejemplos de globs
js/source/1.js
coincide exactamente con el archivo.js/source/*.js
coincide con los archivos que terminen en .js dentro de la carpeta js/source.js/**/*.js
coincide con los archivos que terminen en .js dentro de la carpeta js y dentro de todas sus sub-carpetas.!js/source/3.js
Excluye especificamente el archivo 3.js.static/*.+(js|css)
coincide con los archivos que terminen en .js ó .css dentro de la carpeta static. Existen más patrones, los puedes revisar desde la documentación de la librería minimatch.
Así que tienes la oportunidad de realizar todas las combinaciones posibles, según lo necesites.
Como en nuestra demo, necesitabamos encontrar todos los archivos que terminen en .js dentro de la carpeta js/source, así:
gulp.src('js/source/*.js')
Cada vez que Gulp.js encuentre un archivo que coincida con nuestro patrón, lo irá metiendo dentro de un Stream, que será como una colección de archivos. Claro, respetando las propiedades de cada archivo(ruta, etc).
Entonces podemos decir que tendremos todos esos archivos con sus respectivas propiedades dentro de un Stream, Este Stream puede ser manipulado por Gulp.js.
El método pipe() de Node.js
El método pipe() puede leer, ayudar a transformar y grabar los datos de un Stream.
Es por eso que en nuestro ejemplo usamos el método pipe() 3 veces.
La primera vez lo usamos para leer el Stream y se lo pasamos al plugin “concat” para que este realize la concatenación y así transforme los datos del Stream, así:
.pipe(concat('todo.js'))
La segunda vez lo usamos para leer los datos actuales(js concatenado) y se lo pasamos al plugin “uglify”, para que realize la minificación del archivo concatenado. Todo esto sin escribir en el disco ningún archivo temporal, así:
.pipe(uglify())
La tercera vez se lo pasamos a el método gulp.dest(), así que veamos que hace este método.
gulp.dest()
Canaliza y escribe archivos desde un Stream, por lo que puede canalizar a varias carpetas. Creará las carpetas que no existan y retornará el Stream, por si deseamos realizar alguna acción más.
En pocas palabras, sirve para escribir los datos actuales de un Stream.
Y en nuestro ejemplo lo usamos así:
.pipe(gulp.dest('js/build/'))
Con lo cual escribimos los datos resultantes del Stream dentro de la carpeta js/build/.
El código final nos quedó así:
/*
* Dependencias
*/
var gulp = require('gulp'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify');
/*
* Configuración de la tarea 'demo'
*/
gulp.task('demo', function () {
gulp.src('js/source/*.js')
.pipe(concat('todo.js'))
.pipe(uglify())
.pipe(gulp.dest('js/build/'))
});
Así como realizé 2 tareas consecutivas, con Gulp.js se pueden realizar muchas más.
gulp.watch()
Ver archivos y hacer algo cuando se modifique un archivo. Esto siempre devuelve un EventEmitter que emite los eventos de cambio.
Tiene 2 formas de usar:
gulp.watch(glob, tareas) ó gulp.watch(glob, callback).
gulp.watch('js/source/*.js', ['js']);
Con lo cual, cada vez que se modifique un archivo .js que se encuentre dentro de la carpeta js/source/ automáticamente se ejecutará la tarea js.
gulp.watch('js/source/*.js', function(){
/*
* Aquí iría el código de la acción que deseas realizar,
* Cuando hayan cambios en dichos archivos.
*
* También podrías ejecutar una tarea mediante el método
* gulp.start('js')
*
* Pero este método no es oficial, le pertenece al
* paquete 'Orchestrator' ya que Gulp hereda los
* métodos de 'Orchestrator'.
*/
});