Estos Plug-ins los podemos utilizar en conjunto con los navegadores web de hoy en día, gestores de contenido como Wordpress, Joomla o Drupal e incluso con editores de texto o reproductores de media. Pero como desarrolladores de aplicaciones siempre miramos más allá, y pensamos: ¿Por qué no desarrollar nuestro propio Plug-in? Y la respuesta es simple, con Grunt lo podemos hacer.
Requisitos
Antes de pasar a la teoría detrás del desarrollo de Plug-ins dentro de Grunt, veamos que necesitamos para el desarrollo de este tutorial:
Estructura de nuestro Plug-in
Un Plug-in sencillo en Grunt consiste de un directorio de tareas el cual incluye uno o más archivos JavaScript que definen las mismas, donde estos archivos poseen la misma estructura que un archivo Gruntfile. Dentro del directorio de tareas podemos encontrar otro directorio llamado lib el cual contendrá cualquier número de funciones u objetos para ayudarnos en el funcionamiento de nuestro Plug-in.
Nuestro Plug-in también contiene los archivos usuales que hemos estado utilizando, el package.json y el Gruntfile.js, donde este último contiene una línea que carga las tareas del directorio del mismo nombre, lo cual hace nuestro Plug-in mucho más sencillo de probar.
Creando la estructura
Ya que sabemos cómo están estructurado los Plug-ins vamos a construir la estructura para nuestro ejemplo. En este caso vamos a crear un Plug-in sencillo que abrirá en el navegador especificado un archivo, no es algo muy atrevido pero nos ayudará a ver el proceso de creación para el mismo.
Lo primero es crear un directorio llamado plugin-grunt y dentro del mismo crearemos otro directorio llamado tasks. Vamos a crear ahora nuestro archivo package.json pero esta vez nos ayudaremos de npm para crearlo, para ello solo debemos ejecutar npm init y podremos ingresar los valores para el mismo por consola, veamos este proceso:
Ya con nuestro archivo package.json creado solo nos queda instalar grunt dentro de nuestro directorio para poder probar nuestro Plug-in, para ello usamos npm junto al parámetro save para guardar las dependencias en el package.json. Para finalizar con los ajustes iniciales veamos la respuesta de la consola al ejecutar el comando:
Construyendo la lógica de nuestro Plug-in
Para realizar nuestro ejemplo vamos a necesitar que Grunt puede ejecutar un programa externo, debemos detectar en que sistema operativo estamos ejecutando el Plug-in ya que la idea es hacer el mismo lo más eficaz posible y por último es necesario que podamos llamar un archivo externo desde Grunt. Todo esto puede sonar bastante complicado en un inicio, pero la verdad es que Grunt y valiéndonos de los módulos de Node.js podemos hacerlo sin muchas complicaciones.
Lo primero que haremos será crear dentro de tasks/lib nuestro archivo llamado lanzador_chrome.js, este se encargará de encapsular toda la lógica para ejecutar Google Chrome, veamos el código base para luego explicar su contenido:
module.exports.init = function(grunt){ var exports = {}; return(exports); };Primero usamos module.exports para definir que objetos y funciones este módulo expone, se define una función que retorna un objeto JavaScript que creamos lo cual nos permite tener tanto métodos públicos como privados. Nuestro programa principal usará este módulo al requerirlo y llamar a la función init(), la cual retornará el objeto representado en el exports.
Luego de tener nuestra estructura inicial debemos crear la función que crea el comando basado en el sistema operativo que el usuario está utilizando, para ello utilizaremos el método de Node llamado process.platform() para detectar el mismo. Veamos como luce ahora nuestro lanzador_chrome.js con las modificaciones realizadas:
module.exports.init = function(grunt){ var exports = {}; var crearComando = function(file){ var command = ""; var linux = !!process.platform.match(/^linux/); var windows = !!process.platform.match(/^win/); if(windows){ command = 'start chrome ' + file; }else if (linux){ command = 'google-chrome "' + file + '"'; }else{ command = 'open -a "Google Chrome" ' + file; } return(command); }; return(exports); };Como vemos para cada sistema operativo creamos el comando respectivo para abrir el navegador y retornamos dicho comando al final de nuestra función. Para finalizar con nuestro archivo nos falta definir el método público que llamaremos abrir() el cual se valdrá del método exec() de Node para ejecutar Google Chrome, el cual toma el comando que deseamos ejecutar como el primer argumento y la función de tipo callback a ejecutar cuando el comando termine como el segundo. Veamos entonces el aspecto final de nuestro código para el archivo lanzador_chrome.js:
module.exports.init = function(grunt){ var exports = {}; var crearComando = function(file){ var command = ""; var linux = !!process.platform.match(/^linux/); var windows = !!process.platform.match(/^win/); if(windows){ command = 'start chrome ' + file; }else if (linux){ command = 'google-chrome "' + file + '"'; }else{ command = 'open -a "Google Chrome" ' + file; } return(command); }; exports.abrir = function(file, done){ var comando, process, exec; comando = crearComando(file); grunt.log.writeln('Ejecutando el comando... ' + comando); exec = require('child_process').exec; process = exec(comando, function (error, stdout, stderr) { if (error) { if(error.code !== 0){ grunt.warn(stderr); grunt.log.writeln(error.stack); } } done(); }); }; return(exports); };En la función abrir() lo que hacemos es usar la función crearComando() para obtener el comando de acuerdo al sistema operativo que estamos ejecutando para luego ejecutarlo y luego en el callback verificamos si nuestro proceso funcionó. Adicionalmente, podemos darnos cuenta que este método se encuentra enlazado al módulo que estamos exportando, esto lo que le permitirá hacerlo visible para las tareas de Grunt.
Ya tenemos nuestra lógica construida, ahora solo nos faltaría hacerlo disponible para Grunt.
Definir la tarea en Grunt
Nuestra tarea de Grunt necesita tomar el nombre del archivo como argumento y luego invocar el método abrir(), el cual acabamos de crear. Para ello vamos a crear dentro de nuestro directorio tasks el archivo abrir_con_chrome.js el cual contendrá el siguiente código inicial:
'use strict'; module.exports = function(grunt) { };Una vez definido esto debemos hacer require de nuestro módulo que acabamos de crear y definir nuestra tarea. Para ello usamos el método registerTask e invocamos el método abrir(), enviándole el nombre del archivo para la tarea junto con la referencia de la función done, veamos el aspecto final para nuestro archivo:
'use strict'; module.exports = function(grunt) { var lanzadorChrome = require('./lib/lanzador_chrome.js').init(grunt); grunt.registerTask('abrir', 'Abre el archivo o la URL con el navegador Chrome', function(file){ var done = this.async(); lanzadorChrome.open(file, done); } };Ya hemos terminado con la construcción de nuestro plugin, pero para poder probar su funcionalidad nos falta construir el famoso archivo Gruntfile.js el cual se encarga de cargar nuestras tareas, veamos su contenido:
'use strict'; module.exports = function(grunt) { grunt.loadTasks('tasks'); };Ahora solo nos queda probar nuestro Plug-in, para ello en nuestra consola debemos ejecutar el siguiente comando:
grunt abrir:Gruntfile.jsEl cual automáticamente nos abrirá el contenido de nuestro archivo en una ventana del navegador Google Chrome y en la consola nos indicará cual es el comando que se utilizó:
Si queremos ir un poco más allá veamos como nuestro Plug-in es multiplataforma y nos funciona también en Linux, para ello estando en el directorio del mismo realizamos la llamada al archivo Gruntfile.js, veamos:
Incluso somos capaces de abrir directamente una página web, para ello en vez del nombre del archivo le enviamos como argumento la URL:
Como vemos no está nada mal para nuestro Plug-in, el cual puede abrir cualquier archivo que le especifiquemos sin importar el sistema operativo e incluso cualquier página web que le indiquemos.
Así finalizamos el tutorial, donde pudimos construir un sencillo Plug-in con Grunt, utilizando las herramientas que el mismo pone a nuestra disposición junto con los módulos de Node, queda de nuestra parte investigar un poco más acerca de estos e incluso si somos atrevidos desarrollar alguno más complejo y subirlo a los repositorios de npm para ayudar a la comunidad de Grunt.