Primeros pasos con Gulp

3
24730

Este tutorial pretende ser una iniciación a la herramienta Gulp, que sirve para automatizar tareas en proyectos Javascript. En él veremos como instalarlo y como hacer una primera aplicación de ejemplo en Javascript que utilice Gulp para realizar unas sencillas tareas.

Índice de contenidos.

  1. Introducción.
  2. Entorno.
  3. Instalación.
  4. Funcionamiento.
  5. Gulp API.
  6. El gulpfile.
  7. Ejemplo de uso.
  8. Plugins.
  9. Conclusiones.
  10. Referencias.

1. Introducción.

Con el auge de los nuevos frameworks javascript como AngularJs, Backbone, EmberJs, React, Polymer, etc. surge la necesidad de herramientas para gestionar los proyectos.

Las arquitecturas basadas en estos frameworks necesitan un build system, es decir, es una colección de tareas que automatizan un trabajo repetitivo. De manera análoga en Java tendríamos Maven o en Groovy tendríamos Gradle.

Los componentes más comunes en un flujo típico de front-end son:

  • Gestores de dependencias
  • Preprocesadores (opcionalmente)
  • Herramientas para la construcción de tareas

Gulp es una herramienta para la gestión y automatización de tareas en Javascript. Estas tareas serán de uso común para los desarrolladores. El único requisito para utilizar Gulp es tener instalado Node.js

Algunas de las tareas más frecuentes son:

  • Compilación de CSS y Javascript preprocesado
  • Concatenación
  • Minificación
  • Lanzar un servidor para la recarga automática en el browser
  • Creación de una build para despliegue
  • Ejecución de tests unitarios
  • Ejecución de herramientas para detectar errores en el código
  • Gestionar el código en un repositorio

2. Entorno.

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Mac Book Pro 15″ (2,3 Ghz Intel Core i7, 16 GB DDR3)
  • Sistema Operativo: Mac OS X El Capitan
  • NodeJS v4.4.3
  • npm 3.8.3
  • Atom 1.7.1
  • Gulp 3.9.0

3. Instalación.

Abrimos un terminal e instalamos Gulp de manera global con el siguiente comando.

sudo npm install --global gulp-cli

4. Funcionamiento.

A diferencia de otras herramientas de este tipo, como Grunt, Gulp utiliza streams para la ejecución de tareas. Un stream es un flujo de datos para el intercambio de información. Gulp utiliza el módulo Stream de Node.js. Internamente utiliza la librería vinyl-fs para leer y escribir en un stream.

Además se utilizan tuberías(pipes) para realizar operaciones sobre un stream. Las tuberías son un mecanismo para la comunicación y sincronización entre procesos.

  • Basados en el patrón productor/consumidor
  • Están implementadas de forma muy eficiente en los sistemas operativos
  • Inician todos los procesos al mismo tiempo
  • Atienden automáticamente los requerimientos de lectura de datos para cada proceso cuando los datos son escritos por el proceso anterior

5. Gulp API.

El API de Gulp es muy sencillo y consta 4 instrucciones básicas: gulp.task(), gulp.src(), gulp.dest() y gulp.watch().

codigo gulp

  1. gulp.task()
    • Define una tarea
    • Tiene 3 argumentos:
      • el nombre de la tarea
      • dependencias de otras tareas
      • la función a ejecutar
  2. gulp.src()
    • Toma como parámetro una cadena que coincida con uno o más archivos
    • Utiliza patrones que usa el intérprete de comandos de unix(shell)
    • Retorna un stream que puede ser “pipeado” a un plugin adicional ó hacia el método gulp.dest()
  3. gulp.dest()
    • Canaliza y escribe archivos desde un stream
      • Puede canalizar a varias carpetas
      • Creará las carpetas que no existan
      • Retornará el stream, por si deseamos realizar alguna acción más
    • Sirve para escribir los datos actuales de un stream
  4. gulp.watch()
    • Observa archivos y realiza una acción cuando se modifica un archivo
    • Esto siempre devuelve un EventEmitter que emite los eventos de cambio
    • Tiene 2 formas de uso:
      • gulp.watch(glob, [tasks])
      • gulp.watch(glob, callback)
    • El primer parámetro es un glob con la ruta o un patrón con uno o varios ficheros
    • codigo gulp

    • El segundo puede ser una lista de tareas o una función a ejecutar
    • codigo gulp

    A partir de gulp 4 se incluyen algunas instrucciones nuevas. Las más relevantes son gulp.series() y gulp.parallel() para facilitar la definición de flujos de tareas. Aunque esto se podía realizar en la versión anterior de Gulp, de esta manera el código queda más claro y flexible.

  5. gulp.series()
    • Realiza ejecución de tareas de manera secuencial
    • Tiene de 2 argumentos:
      • El nombre de la/s tarea/s a ejecutar
      • Una función a ejecutar (opcional)
  6. gulp.parallel()
    • Realiza ejecución de tareas en paralelo
    • Tiene de 2 argumentos:
      • El nombre de la/s tarea/s a ejecutar
      • Una función a ejecutar (opcional)

    6. El gulpfile.

    En primer lugar debemos de crear un directorio para el proyecto y dentro crear un módulo de node de la siguiente manera.

    mkdir <nombre_proyecto>
    cd <nombre_proyecto>
    npm init

    A continuación completamos los valores que se solicitan (para el ejemplo basta con el nombre, versión y el entry point) para rellenar el package.json

    codigo gulp

    Después ejecutamos el siguiente comando de npm para instalar gulp en el proyecto y guardarlo en el package.json.

    npm install gulp --save-dev

    Cuando se bajen dependencias de npm se guardarán por defecto en el directorio node_modules en la raíz de nuestro proyecto, y se incluirán dentro del package.json.

    Posteriormente debemos crear un fichero llamado gulpfile.js, que debe de estar en la raíz del proyecto (y estar referenciado en la propiedad main del package.json de nuestro proyecto).

    • El gulpfile debe de contener una estructura con los siguientes elementos:
      • La importación de otros módulos
      • La importación de un fichero de configuración del proyecto (opcional)
      • La definición de las tareas
      • Observadores que se ejecutan en función de ciertos cambios (opcional)
      • Una tarea por defecto a ejecutar
    • Si el gulpfile del proyecto es muy grande conviene modularizarlo en varios ficheros más pequeños por funcionalidad e importarlos
    • En una arquitectura donde tengamos muchos módulos similares conviene externalizar las tareas comunes en un módulo de Node.js para no repetir las mismas tareas en muchos puntos y que éstas puedan divergir.
    • 7. Ejemplo de uso.

      Una vez que tenemos creados el package.js y el gulpfile.js podemos definir nuestra primera tarea (la tarea por defecto).

      codigo gulp

      var gulp = require('gulp');
      
      gulp.task('default', function() {
          console.log('Hello world!');
      });
      

      codigo gulp

      A continuación vamos a ver cómo podemos crear una tarea para minificar código en javascript. Para empezar, vamos a crear un código de prueba simulando el que sería en código real de nuestra aplicación. Creamos un directorio src/js para guardar ahí nuestros ficheros de código. Los llamaremos file1.js y file2.js y contendrán lo siguiente:

      file1.js

      function add(paramA, paramB) {
        console.log('This is a sum');
        return paramA + paramB;
      }
      

      file2.js

      function substract(arg0, arg1) {
        console.log('This is a substract');
        return arg0 - arg1;
      }
      

      A continuación modificaremos nuestro gulpfile para añadir la tarea minify, que deberá concatenar y ofuscar el código. Para ello haremos uso de dos plugins de gulp, que son gulp-concat y gulp-uglify, que se importan en el gulpfile mediante require.

      npm install --save-dev gulp-concat
      npm install --save-dev gulp-uglify
      

      var gulp = require('gulp');
      var concat = require('gulp-concat');
      var uglify = require('gulp-uglify');
      
      gulp.task('minify', function() {
        console.log('minifying js ...');
      
        return gulp.src('src/js/*.js')
          .pipe(concat('all.js'))
          .pipe(gulp.dest('build/js/'));
      });
      
      gulp.task('default', function() {
          console.log('Hello world!');
      });
      

      Para empezar concatenamos el código tomando como fuente todos los ficheros javascript del directorio src/js, escribiendo el resultado en el fichero all.js en el directorio build.

      codigo gulp

      Ahora modificamos la tarea para además de concatenar, ofuscar el código.

      gulp.task('minify', function() {
        console.log('minifying js ...');
      
        return gulp.src('src/js/*.js')
          .pipe(concat('all.js'))
          .pipe(uglify())
          .pipe(gulp.dest('build/js/'));
      });
      

      codigo gulp

      En este caso además de ofuscar el código queremos que se eliminen todos los comentarios que se generan con el console.log(). Para ello hay una propiedad en el plugin uglify que permite hacerlo. En general, cuando utilicemos plugins podemos buscar su documentación, como veremos en el punto siguiente(https://www.npmjs.com/package/gulp-uglify)

      Finalmente añadimos la opción drop_console en el plugin uglify para quitar los comentarios y aprovechamos para introducir un watcher que ejecute la tarea minify y añadimos la dependencia del watcher en la tarea por defecto. De esta manera cuando ejecutemos la tarea gulp se ejecutará el watcher y cuando se modifique algún fichero del código fuente se aplicará la tarea de minificación.

      var gulp = require('gulp');
      var concat = require('gulp-concat');
      var uglify = require('gulp-uglify');
      
      gulp.task('minify', function() {
        console.log('minifying js ...');
      
        return gulp.src('src/js/*.js')
          .pipe(concat('all.js'))
          .pipe(uglify({
            compress: {
              drop_console: true
            }
          }))
          .pipe(gulp.dest('build/js/'));
      });
      
      gulp.task('watch-js', function() {
        gulp.watch('src/js/*.js', ['minify'], function() {
          console.log('watching js changes...');
        });
      });
      
      gulp.task('default', ['watch-js'], function() {
        console.log('Executing gulp...');
      });
      

      Y el resultado final es este..

      codigo gulp

      8. Plugins.

      Gulp tiene alrededor de ~2346 plugins “oficiales” y otros no-oficiales. Los plugins disponibles permiten realizar tareas muy versátiles como:

      • Concatenar ficheros
      • Minimizar js
      • Minimizar imágenes
      • Inyectar dependencias dinámicamente
      • Ejecutar tests
      • Gestionar el código en un repositorio (svn, git)
      • Empaquetar un directorio
      • Ejecutar jshint, htmlhint
      • Generar css ejecutando saas, less, etc.
      • Mostrar ayuda

      En el ejemplo anterior hemos visto los plugins gulp-concat y gulp-uglify. A continuación vamos a mostrar algunos más.

      • gulp-zip
        • Para comprimir directorios con ficheros
        • Permite generar una distribución front-end
        var gulp = require('gulp');
        var zip = require('gulp-zip');
         
        gulp.task('default', function() {
          return gulp.src('src/*')
            .pipe(zip('archive.zip'))
            .pipe(gulp.dest('dist'));
        });
        
      • gulp-if
        • Permite ejecutar una tarea de manera condicional
        var gulpif = require('gulp-if');
        var uglify = require('gulp-uglify');
         
        var condition = true; // TODO: add business logic 
         
        gulp.task('task', function() {
          gulp.src('./src/*.js')
            .pipe(gulpif(condition, uglify()))
            .pipe(gulp.dest('./dist/'));
        });
        
      • gulp-git
        • Para ejecutar comandos sobre un repositorio de código GIT
        • Commit, add, push, clone, etc
        
        var gulp = require('gulp');
        var git = require('gulp-git');
        
        // Run git commit without checking for a 
        // message using raw arguments 
        gulp.task('commit', function(){
          return gulp.src('./git-test/*')
            .pipe(git.commit(undefined, {
              args: '-m "initial commit"',
              disableMessageRequirement: true
            }));
        });
        
        // Run git push 
        gulp.task('push', function(){
          git.push('origin', 'master', function (err) {
            if (err) throw err;
          });
        });
        

      Podemos encontrar documentación de los mismos en:

      9. Conclusiones.

      En los nuevos desarrollos con los frameworks javascript este tipo de herramientas son muy útiles para ayudarnos a ser productivos. Este tipo de herramientas junto con otros elementos del ecosistema javascript, como pueden ser la gestión de dependencias (con npm, bower, jspm, etc.), el uso de frameworks js (AngularJs, Flux, Marionette, etc.) la realización de tests (Jasmine, Protractor) un IDE amigable y ligero (Sublime, Atom, Webstorm, etc.) hacen que el desarrollo en javascript sea algo muy distinto a lo que era hace unos años.

      En particular Gulp (2014) no fue la primera herramienta relevante de este tipo en aparecer, fue Grunt (2012). Aunque Grunt sigue gozando de una gran popularidad, una gran cantidad de plugins desarrollados y una amplia comunidad de desarrolladores, Gulp es superior a Grunt en varios aspectos, lo cual hace que ya en este momento y en el futuro Gulp sea la herramienta de automatización de tareas predominante.

      En primer lugar Grunt tiene un enfoque de configuración sobre código, mientras que Gulp tiene un enfoque de código sobre configuración. Esto hace que Gulp sea mucho más conciso y claro, mientras que Grunt es más verboso. En segundo lugar Gulp es mucho más eficiente, por su concepción basado en el uso de streams implica operaciones de lectura y escritura en memoria. En el caso de Grunt, para encadenar varias operaciones en una tarea se realizan varias lecturas y escrituras en disco.

      Gulp es muy sencillo de aprender, pero en proyectos grandes debemos de tener cuidado en la definición y organización de nuestras tareas y las dependencias entre ellas.

      10. Referencias.

3 COMENTARIOS

  1. Hola Álvaro,

    No me convence mucho lo de instalar con sudo.

    En MacOS y en Linux, la mejor forma de instalar Node (npm), es mediante nvm. De esta forma, no se instalará en carpetas que necesitas permisos de root.

    El problema de instalarlo con sudo, es que todo lo que se baje en node_modules será con otro propietario. Y luego cada dos por tres te pedirá que lo ejecutes con sudo, porque si no no tiene permisos.

    Luego podrás usar npm install –global gulp-cli porque el node_modules de global sí tendrás permiso para escribir en él.

  2. Hola Juan Antonio,

    Me parece muy bien tu comentario. Se podría mejorar el paso de la instalación haciéndolo con nvm como dices. Aunque para el alcance de este tutorial me he centrado más en lo que es en el API en sí y cómo se definen las tareas. Aprovecho tu sugerencia para recomendar el tutorial de Rubén Aguilera (https://www.adictosaltrabajo.com/tutoriales/como-montar-un-entorno-de-desarrollo-para-typescript/) donde esta parte lo hace de manera similar a lo que comentas.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad