Primeros pasos con Browserify

Me apetece hablar un poco hoy sobre Browserify, el cual es sin duda uno de los paquetes más populares que podemos encontrar en npm. Hace algo de tiempo que conozco está herramienta, pero no fue realmente hasta hace poco que decidí probarla, y creo firmemente que ha entrado dentro de mis imprescindibles del día a día.

Browserify es básicamente una herramienta que nos permite gestionar dependencias en forma de módulos del lado del cliente (en el navegador). A grandes rasgos podemos decir que lo que nos permite es crear y requerir módulos tal y como hacemos con Node.js; y digo a grandes rasgos porque no es exactamente así, ya que con browserify no tendremos en el build final varios ficheros javascript, sino que crearemos un bundle con la herramienta y tan solo tendremos un fichero javascript.

Pero vayamos por partes, primero que nada deberás de tener instalada la herramienta de manera global con npm:

npm install -g browserify

Una vez hecho esto, podremos ejecutar directamente browserify desde nuestra terminal, es importante que la instalación haya sido global, o en caso contrario no funcionará, puedes hacer la prueba pidiendo la versión del paquete:

browserify -v

Si te ha devuelto un numero (en mi caso 8.1.3) es que todo está correcto.

Vamos a crear ahora nuestro primer modulo, que, para no perder las buenas costumbres será el típico “hello world”. Lo llamaremos hello.js:

var str = "hello world"; 
module.exports = function(name){ 
    console.log(str + ' ' + name); 
};

Ahora creamos otro modulo en el mismo directorio llamado index.js, y hacemos require al modulo hello.js:

var hello = require('./hello.js'); 
hello('Juan'); //hello world Juan

Todo listo para crear nuestro archivo final, ahora simplemente debemos de ir a nuestro terminal y ejecutar browserify indicandole el archivo de entrada, que es el script desde donde tiene que empezar a “seguir” todas las dependencias, y el nombre del fichero final que debe generar, así:

browserify index.js > bundle.js

Esto nos generará un script llamado bundle.js (o el nombre que tu quieras) que es el que debemos incluir en nuestro fichero HTML como cualquier otro script. Como podrás ver, el proceso de gestión de módulos es automático, browserify se encarga de seguir todas las dependencias e incluirlas dentro del archivo final, fácil y rápido.

¿Que pasa si hacemos require dos veces al mismos script?

Si lo que te preocupa es que se incluya un modulo varias veces, puedes estar tranquilo, browserify incluirá el modulo solo una vez en el bundle final, y luego tu podrás usarlo las veces que quieras.

Hagamos una prueba, volvamos a nuestro modulo hello.js y añadamosle algunos logs:

var str = "hello world"; 
console.log('Soy el modulo hello.js'); 
module.exports = function(name){ 
    console.log(str + ' ' + name); 
};

Y en el index.js hacemos require de hello.js en dos ocaciones:

var hello = require('./hello.js'); //Soy el modulo hello.js
hello('Juan'); //hello world Juan 
var helloAgain = require('./hello.js');

Genera nuevamente el bundle y carga tu html, comprobarás que la frase “Soy el modulo hello.js” solo se ha visto una vez en la consola, no dos. De esta forma tu puedes usar el mismo modulo tantas veces como quieras, cargando por ejemplo hello.js en varios módulos diferentes, sin tener que preocuparte de que se ejecute cada vez las rutinas del modulo hello.js, o de que el peso del archivo final sea excesivamente grande.

¿Que pasa con el ámbito de las variables?

Otra de las ventajas de browserify es que cada modulo será totalmente independiente, quedando todo lo que hagas encapsulado y exponiendo hacia el exterior solo lo que tu quieras que sea expuesto mediante module.exports.

Si quieres, puedes hacer una prueba muy sencilla con lo que ya tenemos hecho, si te fijas en hello.js se ha declarado la variable str, no se encuentra dentro de ninguna función, pero solo tiene validez dentro de ese modulo. Para comprobarlo vamos a añadir a index.js lo siguiente:

var hello = require('./hello.js'); //Soy el modulo hello.js
hello('Juan'); //hello world Juan 

//Comprobando el scope 
if(str){ 
    console.log('Esto no se ejecutará nunca');
}else{ 
    console.log('la variable str no existe en este módulo, porque pertenece al módulo hello.js'); 
}

Como dice el ejemplo, la frase “Esto no se ejecutará nunca” no la verás nunca en la consola. De esta forma, cualquier variable que declares, o función que definas tendrá validez solamente dentro del scope del propio módulo, por lo que puedes declarar lo que tu quieras fuera de funciones o closures sin miedo a contaminar el objeto window.

Usando modulos de npm

Otra de las grandes ventajas de usar browserify es que puedes usar módulos instalados desde npm en el navegador. Por ejemplo jQuery, vamos a instalarlo mediante npm:

npm install jquery

Ahora simplemente debemos hacerle require sin indicar path, y lo tendremos incluido en nuestro bundle.

var $ = require('jquery'); 
$(function(){ 
    console.log('jQuery'); 
});

Si usamos npm para gestionar todos los módulos que podamos, nos facilitaremos la vida a nosotros mismos, porque no deberemos de preocuparnos por nada más que no sea hacer un npm install o un npm update para mantener en orden y funcionando nuestras aplicaciones.

Vigilando los cambios con Watchify

Watchify es otra herramienta que nos hará la vida más fácil cuando trabajemos con browserify. Este paquete se encarga de vigilar todos los cambios que ocurren en nuestro código y generar el bundle cada vez que exista una modificación. Para instalarlo, volveremos a recurrir a npm:

npm install -g watchify

En este caso la instalación ha de ser también usando -g para que este disponible globalmente. Y la forma de usarlo es casi identica a como usamos browserify:

watchify index.js -o bundle.js

Con esta ultima sentencia le estamos indicando que use como punto de entrada index.js vigilando todos los cambios en este script y en todos los referenciados mediante require de todo el árbol de dependencias, si alguno de estos se modifica, se generaría nuevamente el bundle.js sin que tengamos que intervenir.

Usando Gulp y Browserify

Aunque trabajar con browserify y watchifiy es bastante sencillo, aún podríamos facilitarlo más relegando todo el trabajo a un gestor de tareas como Gulp, si no lo conoces te recomiendo leer este post.

Primero que nada vamos a instalar los 4 módulos que usaremos como dependencias de desarrollo (asegurate de tener el package.json de tu proyecto).

npm install gulp browserify watchify vinyl-source-stream --save-dev

Y creamos nuestro gulpfile.js en la raíz del proyecto:

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

Ahora pasaremos a iniciar browserify y watchify, pero según la documentación de watchifiy al crear la instancia de browserify debemos de pasarle un objeto con 3 propiedades de manera obligada, estas propiedades son, cache, packageCache, y fullPaths, aunque la manera más sencilla es pasarle directamente el objeto args de watchify, que es justo lo que haremos:

var entry = './index.js'; //Script de entrada 
var args = watchify.args; 
args.debug = true; //Genera el sourcemap para debuguear
args.fullPaths = false; //Evita el uso de paths absolutos 
var bundler = watchify(browserify(entry, args));

Una vez creada la instancia con los parámetros que queremos, pasamos a gestionar el bundle y las tareas:

function createBundle(){ 
    console.log('Now building...'); 
    return bundler.bundle() 
        .pipe(source('bundle.js')) //Nombre del bundle final
        .pipe(gulp.dest('./build')); //Directorio de destino
} 

gulp.task('dev', createBundle); 
bundler.on('update', createBundle); //A cada modificación generamos un nuevo bundle 

//Proporcionamos algo de información al generar el bundle
bundler.on('time', function(time){ 
    console.log('Done at ' + (time/1000)); 
});

Y listo, a estas alturas el archivo gulpfile.js tendría que verse más o menos así:

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

var entry = './index.js'; //Script de entrada 
var args = watchify.args; 
args.debug = true; //Genera el sourcemap para debuguear
args.fullPaths = false; //Evita el uso de paths absolutos 

var bundler = watchify(browserify(entry, args)); 

function createBundle(){ 
    console.log('Now building...'); 
    return bundler.bundle() 
        .pipe(source('bundle.js')) //Nombre del bundle final
        .pipe(gulp.dest('./build')); //Directorio de destino
} 

gulp.task('dev', createBundle); 
bundler.on('update', createBundle); //A cada modificación generamos un nuevo bundle 

//Proporcionamos algo de información al generar el bundle
bundler.on('time', function(time){ 
    console.log('Done at ' + (time/1000)); 
});

Ahora cuando trabajemos en nuestro proyecto solo deberemos de ejecutar gulp dev en la terminal, y olvidarnos de todo, que gulp y browserify se encargarán de hacer todo el trabajo repetitivo, y nosotros solo tendremos que centrarnos en generar buen código ;)

Si te interesa el tema, te animo a que busques más información en google, porque esto que ves aquí es solo una de las pocas formas que existen de trabajar con browserify, sobre todo no te pierdas “Browserify Handbook“.

Anímate y comenta que opinas de browserify, si lo has usado o planeas usar, si lo usas con algunos módulos, o cualquier otra cosa relacionada, cualquier comentario es bienvenido.