Conoce Grunt: La herramienta de construcción para JavaScript
Spanish (Español) translation by Victor Manuel (you can also view the original English article)
Si estás trabajando en un proyecto grande, sin duda tendrás un script de construcción o un montón de scripts de tareas para ayudar con algunas de las partes repetitivas del proceso. Puedes usar Ant o Rake, dependiendo del lenguaje en el que esté escrito el proyecto.
Pero, ¿qué podemos usar si el proyecto es principalmente JavaScript? Ese es el problema que Ben Alman se propuso resolver cuando creó Grunt.
¿Qué es Grunt?
¿Qué es exactamente Grunt? Bueno, el LÉEME en Github dice
Grunt es una herramienta de construcción de línea de comandos basada en tareas para proyectos de JavaScript.
Esta es la idea: cuando se trabaja en un proyecto de JavaScript, hay un montón de cosas que querrás hacer regularmente. ¿Cómo qué?, te preguntarás, bueno, como concatenar determinados archivos, ejecutar JSHint en tu código, ejecutar pruebas o minificar tus scripts. Si estás pegando tu JavaScript en JSHint en línea, probablemente se da cuenta de que hay una mejor manera de hacerlo; incluso si estás usando cat para concatenar archivos o un minificador de línea de comandos, sería bueno tener un solo conjunto de comandos unificado para todas esas tareas adicionales, que funcionara para cada proyecto de JavaScript, ¿verdad?
Eso es lo que pretende ser Grunt. Tiene un montón de tareas incorporadas que te llevarán bastante lejos, con la posibilidad de construir tus propios plugins y scripts que amplíen la funcionalidad básica.
Para más información sobre la introducción de Grunt, consulta el post de Ben en su blog personal y en el blog de Bocoup.
¿Cómo instalo Grunt?
Grunt está construido sobre Node.js, y está disponible como paquete a través del gestor de paquetes de Node (npm). Querrás instalarlo globalmente, así que usa este comando:
1 |
npm install -g grunt |
Notarás que instala bastantes dependencias; hay otros paquetes npm que Grunt utiliza. Una vez hecho esto, ¡ya está todo listo!
¿Cómo uso Grunt?
Como sabes, Grunt es una herramienta de línea de comandos; por lo tanto, asumiré que tienes una ventana de terminal abierta para el resto de este tutorial.
Empecemos creando un directorio de proyecto de ejemplo; en realidad no vamos a construir un proyecto aquí, sino que veremos cómo funciona Grunt en este directorio. Una vez que estés dentro de ese directorio, ejecuta el comando grunt (según la documentación, si estás en Windows, puede que tengas que ejecutar grunt.cmd). Probablemente verás algo como esto:
1 |
<FATAL> Unable to find 'grunt.js' config file. Do you need any --help? </FATAL> |
Antes de que puedas aprovechar todo el potencial de Grunt, vas a necesitar un archivo grunt.js en el directorio del proyecto. Afortunadamente, Grunt puede auto-generar un archivo grunt.js y algún otro material de esqueleto del proyecto con la tarea init, que puede ejecutarse sin un archivo grunt.js en su lugar. Pero el grunt init todavía no es suficiente para poner en marcha tu proyecto, como verás si lo ejecutas. Necesitas elegir un tipo de proyecto para generar. Ejecutar grunt init te dará una lista de tipos de proyectos para elegir:
-
jquery: Un plugin de jQuery -
nodo: Un módulo de nodo -
commonjs: Un módulo de CommonJS -
gruntplugin: Un plugin de Grunt -
gruntfile: Un Gruntfile (grunt.js)
Si tu proyecto no se ajusta a ninguno de los cuatro primeros tipos de proyecto, puedes utilizar el último: gruntfile: simplemente crea un grunt.js básico que puedas rellenar. Así que, vamos a probar esto, con la plantilla del plugin jQuery. Ejecuta grunt init:jquery en tu terminal.
Notarás que hay un montón de salida inicial. Si te tomas el tiempo de leer las notas de la plantilla, verás que vamos a tener que rellenar algunos valores, como el nombre y el título del proyecto. De hecho, después de esa nota, verás algo como esto:
1 |
Please answer the following: |
2 |
[?] Project name (jquery.demo) |
Cada vez que inicialices un proyecto, Grunt te hará una serie de preguntas, para poder rellenar algunas opciones. ¿Ese valor en el paréntesis? Es la sugerencia por defecto, basada en el tipo de proyecto y el nombre del directorio del proyecto. Si quieres cambiarlo, escribe tu propio nombre de proyecto al final de la línea y pulsa 'enter'; de lo contrario, simplemente pulsa 'enter' para usar el nombre por defecto.
Continúa y rellena el resto de los campos. Para un proyecto de plugin jQuery, esto es lo que más necesitarás para darle:
- Título del proyecto
- Descripción
- Versión
- Repositorio git del proyecto
- Página principal del proyecto
- Rastreador de problemas del proyecto
- Licencias
- Nombre del autor
- Correo electrónico del autor
- Url del autor
- Versión requerida de jQuery
Muchos de estos tienen valores por defecto; si quieres usar el valor por defecto, simplemente pulsa enter para esa línea; para dejar el campo en blanco, puedes simplemente escribir "ninguno". Una vez que hayas pasado por todas las opciones, verás que Grunt está creando algunos archivos de proyecto básicos. ¿Cómo qué? Como esto:
1 |
LICENSE-GPL |
2 |
LICENSE-MIT |
3 |
README.md |
4 |
grunt.js |
5 |
libs |
6 |
|-- jquery |
7 |
| |-- jquery.js |
8 |
|-- qunit |
9 |
|-- qunit.css |
10 |
|-- qunit.js |
11 |
package.json |
12 |
src |
13 |
|-- jquery.demo.js |
14 |
test
|
15 |
|-- jquery.demo.html |
16 |
|-- jquery.demo_test.js |
Como puedes ver, esto nos da un buen comienzo: no sólo tenemos nuestro archivo de plugin (src/jquery.demo.js), también tenemos las pruebas de Qunit (test/jquery.demo_test.js). Y estos no son archivos vacíos, tampoco. Tienen algo de contenido inicial, con un plugin de jQuery tan básico y pruebas unitarias. Ve y revisa el contenido de estos archivos, verás lo que quiero decir.
Grunt hace algo más que configurar el proyecto por ti.
Por supuesto, Grunt hace más que configurar el proyecto por ti. En particular, nuestro proyecto ahora tiene grunt.js: un archivo de configuración específico del proyecto; debido a las opciones que establece, ahora somos capaces de utilizar las otras tareas incorporadas de Grunt. Pronto lo abriremos y haremos algunos ajustes, pero por ahora vamos a ejecutar algunas tareas.
Si ejecutas grunt sin opciones ahora, ejecutaremos la tarea por defecto, si se ha establecido una. En el caso de un proyecto de plugin de jQuery, eso equivale a ejecutar estos cuatro comandos:
-
grunt lint: comprueba su JavaScript contra JSHint -
grunt qunit: ejecuta sus pruebas de Qunit -
grunt concat: concatena los archivos del proyecto y coloca el nuevo archivo en una carpetadist -
grunt min: minifica el archivoconcatpuesto.
Debo señalar algo sobre las pruebas de Qunit aquí: Los tests de Qunit están pensados para ejecutarse en el navegador por defecto; basta con abrir tests/jquery.demo.html (o su equivalente) en el navegador. Sin embargo, la prueba grunt qunit quiere ejecutarlos en el terminal, lo que significa que necesitas tener PhantomJS instalado. No es difícil: simplemente dirígete a phantomjs.org y descarga e instala la última versión. Si Grunt puede encontrarlo en tu ruta, será capaz de ejecutar las pruebas de Qunit desde la terminal.
Por lo tanto, la ejecución de grunt debe darle una salida similar a esta:


Como puedes ver, cada una de nuestras cuatro tareas se ha ejecutado. Si alguna de ellas fallara, el resto de las tareas se cancelarían (a menos que llames a Grunt con la bandera --force).
¿Cómo puedo personalizar mis tareas?
Ya hemos obtenido un montón de grandes funcionalidades de Grunt, usándolo tal y como viene. Sin embargo, vamos a abrir ese archivo grunt.js y hacer alguna configuración.
Dentro de grunt.js, verás que toda la configuración se hace pasando un objeto literal a grunt.initConfig(). Veamos algunas de las propiedades de nuestro objeto config.
Pkg
Esta propiedad apunta al archivo package.json que Grunt creó en el directorio de nuestro proyecto. Tener un archivo package.json es parte de la especificación CommonJS Packages; es un lugar único donde la mayoría de los metadatos sobre el proyecto (nombre, versión, página de inicio, enlace al repositorio ... muchos de los valores que se establecen al inicializar el proyecto) pueden ser almacenados. Sin embargo, esta propiedad pkg hace algo más que apuntar al archivo de paquete: observe la sintaxis: '<json:package.json>'. Esa es una de las directivas incorporadas de Grunt: realmente carga el archivo JSON, por lo que Grunt (o usted) puede acceder a todas las propiedades del archivo package.json desde la propiedad pkg.
meta
La meta de propiedad es un objeto con una sola propiedad: un banner. Este banner es el comentario que va en la parte superior de los archivos de proyecto concatenados o minificados. Como puedes ver, es una cadena con algunas etiquetas de plantilla (<%= %>); en la mayoría de los casos, las etiquetas rodean una llamada a una propiedad de la propiedad pkg, como pkg.title. Sin embargo, también puedes ejecutar funciones desde dentro de esas etiquetas: el uso de grunt.template.today() y _.pluck() nos lo demuestra.
concat / min / qunit / lint/ watch
He agrupado las siguientes cinco propiedades porque son muy similares. Todas ellas establecen opciones para tareas específicas, las tareas a las que dan nombre. A la hora de configurar estas tareas, es importante tener en cuenta que Grunt distingue entre dos tipos de tareas: tareas regulares y multitareas. Básicamente, la diferencia es que las tareas normales sólo tienen un único conjunto de opciones de configuración, mientras que las multitareas pueden tener varios conjuntos de instrucciones (llamados objetivos). De las cinco tareas que enumeré en el encabezado de esta sección, la única que no es una multitarea es la inspección.
Observa que en nuestro objeto config, las propiedades qunit y lint son ambos objetos con la propiedad files. files es un único objetivo para esta tarea. En ambos casos, son un array de archivos que se utilizarán al ejecutar esta tarea. Digamos que quiero ser capaz de lint sólo los archivos en el subdirectorio src. Podrías añadir otro objetivo para que la propiedad lint se vea así:
1 |
lint: {
|
2 |
files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'], |
3 |
src: ['src/**/*.js'] |
4 |
},
|
Ahora, para lint sólo los archivos en src, ejecuto grunt lint:src: paso el nombre del objetivo después de dos puntos. Si ejecuto sólo grunt lint, se ejecutarán ambos objetivos.
En el caso de las tareas concat y min, los objetivos son más complicados: son objetos con propiedades de origen (src) y destino (dest). Por supuesto, esto le dice a Grunt dónde obtener los archivos y dónde ponerlos cuando termine de procesarlos, respectivamente. Si agregas otros archivos al proyecto, querrás agregarlos en el lugar correcto para asegurarte que están concatenados y minificados correctamente. Por lo tanto, si agregué un archivo src / utils.js del que dependía mi complemento jQuery, cambiaría concat.dist.src a esto:
1 |
src: ['<banner:meta.banner>', 'src/utils.js', '<file_strip_banner:src/<%= pkg.name %>.js>'], |
Si observas algunas de estas tareas con más detenimiento, notarás algunas otras directivas: la más importante es probablemente la directiva Esto te permite acceder a las propiedades de otras tareas para su reutilización. Notarás que la configuración de la tarea watch utiliza , para que opere sobre la misma lista de archivos que le dimos a la tarea lint. Puedes aprender más sobre las otras directivas en la documentación de Grunt.
Hablando de la tarea del watch, ¿qué hace exactamente? Muy simple: ejecuta las tareas en la propiedad tasks cuando se cambia un archivo en esa lista de archivos. De forma predeterminada, se ejecutan las tareas lint y qunit.
jshint
Esta propiedad simplemente configura qué "partes malas" JSHint busca en su JavaScript. La lista completa de opciones se puede encontrar en las páginas de opciones del sitio web de JSHint.
En la parte inferior de nuestro archivo grunt.js, verás esta línea:
1 |
grunt.registerTask('default', 'lint qunit concat min'); |
Esto es lo que crea nuestra tarea por defecto; ya sabes, la que se ejecuta cuando ejecutamos sólo grunt. En realidad estás creando un alias de tarea, y puedes crear tantos alias de tareas como quieras:
1 |
grunt.registerTask('src', 'lint:src qunit:src concat:src min:src'); |
Asumiendo que has creado objetivos src para cada una de esas tareas, ahora puedes llamar a grunt src y hacer exactamente lo que quieres.
¿Cómo utilizo las tareas de terceros?
Mientras que las tareas que vienen con Grunt te llevarán bastante lejos, probablemente puedes pensar en otras cosas que te encantaría poder automatizar. No te preocupes: Grunt viene con una API que permite a cualquiera crear tareas y plugins de Grunt. Si bien no crearemos ninguna tarea de Grunt en este tutorial, si estás interesado en hacerlo, debe comenzar con la plantilla del complemento Grunt (ejecuta grunt init:gruntplugin) y luego lee los documentos de API. Una vez que hayas escrito tu tarea, puedes cargarla en un proyecto agregando esta línea dentro del archivo grunt.js de tu proyecto:
1 |
grunt.loadTasks(PATH_TO_TASKS_FOLDER); |
Ten en cuenta que el parámetro no es la ruta del archivo de la tarea en sí, sino la ruta de la carpeta en la que se encuentra el archivo de la tarea.
Sin embargo, otros plugins de Grunt están empezando a aparecer, y algunos están disponibles en NPM. Después de instalarlos mediante npm install, los cargarás en tu proyecto con esta línea:
1 |
grunt.loadNpmTasks(PLUGIN_NAME); |
Por supuesto, querrás consultar la documentación del complemento para ver qué debe agregar a su objeto de configuración.
¿Qué plugins de Grunt hay disponibles? Bueno, como Grunt es tan nuevo (tiene menos de un mes mientras escribo esto), todavía no hay muchos. Yo he encontrado dos:
-
grunt-css: para linting y compresión CSS -
grunt-jasmine-task:ejecutar las especificaciones de Jasmine
Si has encontrado otros, me encantaría conocerlos; ¡ponlo en los comentarios!
Conclusión
Aunque Grunt es un proyecto muy nuevo, no está incompleto; como hemos visto, viene con casi todo lo que necesitarás para usarlo en un proyecto grande, y se puede extender tanto como quieras.
Espero que Grunt se convierta en un estándar de la comunidad, y que veamos un montón de tareas, plugins y plantillas init apareciendo en un futuro. ¿Qué te parece?



