Desarrollo

Gulp con Browserify, Watch y servidor Express.

Con Gulp tenemos disponible un build system con streams haciendo uso de las pipes de node.js. Es realmente útil para agilizar el desarrollo de aplicaciones de Javascript y se ha convertido en imprescindible para mí en muy poco tiempo.

En el siguiente post, veremos como crear un archivo de configuración para construir desde los archivos fuente de desarrollo a archivos destino los siguientes tipos:

  • HTML
  • CSS
  • Javascript

En Javascipt definiremos la tarea de browsificar modulos de node.js para cargarlos en el lado del navegador.

El script también incluirá la tarea watch para detectar los cambios en los archivos fuente y un servidor con una función de recarga automática para actualizar el navegador sin necesidad de recargar manualmente el documento.

Empezando, HTML

En primer lugar ejecutamos en el directorio raíz de la aplicación el comando:

$npm init

y esto nos crea un archivo JSON necesario para utilizar gulp con la aplicación.

Creamos un archivo llamado gulpfile.js y en él editaremos las tarea que serán llevadas a cabo por gulp.

Requerimos la carga el módulo llamado gulp:

var gulp = require('gulp');
var source = require('vinyl-source-stream');

Si el módulo no está instalado, hay que instalarlo introduciendo un comando tal como sigue:

$ npm install --save-dev nombredemodulo

y creamos la tarea:

gulp.task('html', function() {
  return gulp.src('./src/html/*.html')
    .pipe(gulp.dest('dist'))
});

En la línea de comandos, para lanzar la tarea:

$gulp html

CSS

La tarea:

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

Node.js con Browserify

En primer lugar, crearemos una tarea para los archivos Javascript browseificados:

var browserify = require('browserify');
var html = require('html-browserify');
var gulpif = require('gulp-if');
var watch;

gulp.task('browserify-watch', function(){
watch = true;
browserifyShare();
});

function browserifyShare(){
var b = browserify({
cache: {},
packageCache: {},
fullPaths: true
});

if(watch) {
b = watchify(b);
b.on('update', function(){
bundleShare(b);
});
}

b.add('./script.js');

bundleShare(b);
}

function bundleShare(b) {
b.bundle()
.pipe(source('./script.js'))
.pipe(gulp.dest('./dist/scripts'))
}


Una tarea para lanzar las demás

Creamos otras tareas para llamar a las otras tareas definidas ahora mismo:

gulp.task('build', ['html', 'styles', 'browserify-watch']);
gulp.task('default', ['build']);

Con esto conseguimos que la tarea por defecto cargue el resto de tareas del script.

Para hacerlo:

$gulp

Observar los cambios: Watch

Hasta ahora, para cada cambio en un archivo fuente hay que ejecutar alguna tarea con el comando gulp, pero existe el modulo watch el cual se encarga de detectar cada cambio en los archivos fuente y ejecutar la tarea necesaria para su construcción sin cargar manualmente la tarea:

Para ello hay que hacer algunos cambios:

var watchify = require('watchify');

gulp.task('watch', ['browserify-watch'], function(){
gulp.watch('src/html/*.html', ['html']);
gulp.watch('src/styles/*.css', ['styles']);
}

gulp.task('build', ['html', 'styles', 'browserify-watch']);
gulp.task('default', ['build', 'watch']);

Instalando el servidor

Creamos las siguientes variables:

var express = require('express');
var server;

Y añadimos la tarea:

gulp.task('server', function() {
server = express();
server.use(express.static('dist'));
server.listen(8000);
browserSync({ proxy: 'localhost:8000' });
});

Abrimos el servidor:

$gulp server

Al iniciar la tarea server se lanzará el servidor y se podrá cerrar en cualquier momento con CTRL+C en la línea de comandos. El navegador accederá a la aplicación Javascript en la dirección localhost:8000

Recarga automática: BrowserSync

Hasta ahora hemos conseguido avances muy majos, pero aún podemos automatizar más el desarrollo de nuestra aplicación. Haremos una función que recarga automáticamente en el navegador los archivos modificados.

var browserSync = require('browser-sync');

Entonces, el servidor queda modificado así:

gulp.task('server', function() {
server = express();
server.use(express.static('dist'));
server.listen(8000);
browserSync({ proxy: 'localhost:8000' });
});

Al ejecutar la tarea, gulp abrirá el navegador por defecto y cargará el recurso en localhost:3000. Esto es así porque BrowserSync tiene incorporado un proxy para nuestros archivos.

Para recargar la tarea al modificar las fuentes, puede crearse esta función:

function reload() {
if (server) {
return browserSync.reload({ stream: true });
}

return gutil.noop();
}

Luego llamamos a la función en tuberías en cada tareas, por ejemplo, en la tarea HTML sería:

gulp.task('html', function() {
return gulp.src('./src/html/*.html')
.pipe(gulp.dest('dist'))
.pipe(reload());
});

para CSS:

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

y para Browserify:

function bundleShare(b) {
b.bundle()
.pipe(source('./script.js'))
.pipe(gulp.dest('./dist/scripts'))
.pipe(gulpif(watch, livereload()))
.pipe(reload());
}

Para llamar al proceso desde la tarea watch, ésta se puede modificar para que quedé así:

gulp.task('watch', ['browserify-watch'], function(){
gulp.watch('src/html/*.html', ['html']);
gulp.watch('src/styles/*.css', ['styles']);

livereload.listen(35729);

Por último, para lanzar gulp el servidor y todas las tareas:

$gulp

Resumen

A modo de resumen, el archivo gulpfile.js quedará así:

'use strict'
var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var express = require('express');
var html = require('html-browserify');
var watchify = require('watchify');
var livereload = require('gulp-livereload');
var gulpif = require('gulp-if');
var browserSync = require('browser-sync');
var notify = require('gulp-notify')
var util = require('gulp-util')
var server;
var watch;

gulp.task('html', function() {
return gulp.src('./src/html/*.html')
.pipe(gulp.dest('dist'))
.pipe(reload());
});

gulp.task('images', function() {
return gulp.src('images/*.svg')
.pipe(gulp.dest('./dist/images'))
.pipe(reload());
});

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

gulp.task('browserify-watch', function(){
watch = true;
browserifyShare();
});

function browserifyShare(){
var b = browserify({
cache: {},
packageCache: {},
fullPaths: true
});

if(watch) {
b = watchify(b);
b.on('update', function(){
bundleShare(b);
});
}

b.add('./script.js');

bundleShare(b);
}

function bundleShare(b) {
b.bundle()
.pipe(source('./script.js'))
.pipe(gulp.dest('./dist/scripts'))
.pipe(gulpif(watch, livereload()))
.pipe(reload());
}

gulp.task('server', function() {
server = express();
server.use(express.static('dist'));
server.listen(8000);
browserSync({ proxy: 'localhost:8000' });
});

gulp.task('watch', ['browserify-watch'], function(){
gulp.watch('src/html/*.html', ['html']);
gulp.watch('src/styles/*.css', ['styles']);

livereload.listen(35729);
});
gulp.task('build', ['html', 'styles', 'browserify-watch']);
gulp.task('default', ['build', 'server', 'watch']);

function reload() {
if (server) {
return browserSync.reload({ stream: true });
}

return util.noop();
}
Hi, I’m Gustavo Moreno