Por qué dejé Gulp y Grunt por scripts npm

Sé lo que estás pensando. ¡¿QUÉ ?! ¿No acaba de matar Gulp a Grunt? ¿Por qué no podemos estar contentos por unos minutos aquí en la tierra de JavaScript? Te escucho, pero ...

Descubrí que Gulp y Grunt son abstracciones innecesarias. Los scripts npm son muy potentes y, a menudo, más fáciles de usar.

Comencemos con un ejemplo ...

Yo era un gran fan de Gulp. Pero en mi último proyecto, terminé con cientos de líneas en mi archivo gulp y alrededor de una docena de complementos Gulp. Estaba luchando por integrar Webpack, Browsersync, recarga en caliente, Mocha y mucho más usando Gulp. ¿Por qué? Bueno, algunos complementos tenían documentación insuficiente para mi caso de uso. Algunos complementos solo expusieron parte de la API que necesitaba. Uno tenía un error extraño en el que solo miraba una pequeña cantidad de archivos. Otro color despojado al enviarlo a la línea de comando.

Estos son problemas que se pueden resolver, pero ninguno de estos problemas ocurrió cuando llamé a las herramientas directamente.

Últimamente he notado que muchos proyectos de código abierto simplemente usan scripts npm. Decidí dar un paso atrás y volver a examinar. ¿Realmente necesitaba a Gulp? Resulta que no lo hice.

Decidí intentar usar solo scripts npm en mi nuevo proyecto de código abierto. Creé un entorno de desarrollo rico y un proceso de compilación para aplicaciones React usando solo scripts npm. ¿Curioso cómo se ve esto? Echa un vistazo a React Slingshot. Explico cómo crear este proceso de compilación utilizando scripts npm en "Creación de un entorno de desarrollo de JavaScript" en Pluralsight.

Lo sorprendente es que ahora prefiero trabajar con scripts npm en lugar de Gulp. Este es el por qué.

¿Qué pasa con Gulp y Grunt?

Con el tiempo, he notado tres problemas centrales con los corredores de tareas como Gulp y Grunt:

  1. Dependencia de los autores de complementos
  2. Depuración frustrante
  3. Documentación inconexa

Consideremos cada uno de estos problemas.

Problema n. ° 1: dependencia de los autores de complementos

Cuando trabaja con tecnologías nuevas o impopulares, es posible que no exista ningún complemento. Y cuando existe un complemento, es posible que esté desactualizado. Por ejemplo, Babel 6 se lanzó recientemente. La API cambió significativamente, por lo que muchos complementos de Gulp eran incompatibles con la última versión. Cuando usaba Gulp, estaba atascado porque el complemento de Gulp que necesitaba aún no estaba actualizado.

Con Gulp o Grunt, debe esperar a que los mantenedores de complementos proporcionen actualizaciones o solucionarlo usted mismo. Esto retrasa su capacidad para utilizar nuevas versiones de herramientas modernas. Por el contrario, cuando utilizo scripts npm, consumo herramientas directamente sin una capa adicional de abstracción . Esto significa que cuando se lanzan nuevas versiones de Mocha, Istanbul, Babel, Webpack, Browserify, etc., puedo utilizar las nuevas versiones de inmediato.

En términos de selección, nada supera a npm:

Cuando usa scripts npm, no busca un complemento Grunt o Gulp. Puede elegir entre más de 227.000 paquetes npm.

Para ser justos, si el complemento Grunt o Gulp que necesita no está disponible, ciertamente puede utilizar los paquetes npm directamente. Pero entonces ya no estás aprovechando Gulp o Grunt para esa tarea específica.

Problema # 2: Depuración frustrante

A medida que fallan las integraciones, la depuración en Grunt y Gulp puede resultar frustrante. Dado que está trabajando con una capa adicional de abstracción, existen más causas potenciales para cualquier error:

  1. ¿Está rota la herramienta base?
  2. ¿Está roto el complemento Grunt / Gulp?
  3. ¿Mi configuración está rota?
  4. ¿Estoy usando versiones incompatibles?

El uso de scripts npm elimina # 2. Y encuentro que el número 3 es mucho menos común, ya que normalmente llamo directamente a la interfaz de línea de comandos de la herramienta. Finalmente, el # 4 es menos común, ya que reduje la cantidad de paquetes en mi proyecto utilizando npm directamente en lugar de usar la abstracción de un ejecutor de tareas.

Problema n. ° 3: documentación inconexa

La documentación de las herramientas principales que necesito es casi siempre mejor que los complementos asociados de Grunt y Gulp. Por ejemplo, si uso gulp-eslint, termino dividiendo mi tiempo entre los documentos de gulp-eslint y el sitio web de ESLint. Tengo que cambiar el contexto entre el complemento y la herramienta que está abstrayendo. La pieza central de fricción en Gulp y Grunt es esta:

Comprender la herramienta no es suficiente. Gulp y Grunt requieren que comprenda la abstracción del complemento.

La mayoría de las herramientas relacionadas con la compilación ofrecen interfaces de línea de comandos claras, potentes y bien documentadas. Consulte los documentos de la CLI de ESLint como un buen ejemplo. Encuentro la lectura e implementación de una llamada de línea de comando corta en scripts npm más clara, menos fricción y más fácil de depurar (ya que se eliminó una capa de abstracción).

Ahora que he establecido los puntos débiles, la pregunta es, ¿por qué creemos que necesitamos corredores de tareas como Gulp y Grunt?

¿Por qué hemos ignorado npm para las compilaciones?

Creo que hay cuatro conceptos erróneos fundamentales que llevaron a que Gulp y Grunt se volvieran tan populares:

  1. La gente piensa que los scripts npm requieren fuertes habilidades de línea de comandos
  2. La gente piensa que los scripts npm no son lo suficientemente potentes
  3. La gente cree que las transmisiones de Gulp son necesarias para compilaciones rápidas
  4. La gente piensa que los scripts de npm no se ejecutan en varias plataformas

Abordemos estos conceptos erróneos en orden.

Concepto erróneo n . ° 1 : los scripts npm requieren habilidades sólidas en la línea de comandos

No es necesario que sepa mucho sobre la línea de comandos de su sistema operativo para disfrutar del poder de los scripts npm. Claro, grep, sed, awk y pipe son habilidades de toda la vida que vale la pena aprender, pero no es necesario ser un asistente de línea de comandos de Unix o Windows para usar scripts npm . Puede aprovechar los miles de paquetes en npm para hacer el trabajo en su lugar.

Por ejemplo, es posible que no sepa que en Unix esto borra forzosamente un directorio: rm -rf. Esta bien. Puede usar rimraf que hace lo mismo (y funciona multiplataforma para arrancar). La mayoría de los paquetes npm ofrecen interfaces que asumen muy poco conocimiento de la línea de comandos de su sistema operativo. Simplemente busque en npm paquetes que hagan lo que necesita, lea los documentos y aprenda sobre la marcha. Solía ​​buscar complementos de Gulp. Ahora busco paquetes npm en su lugar. Un gran recurso: libraries.io.

Concepto erróneo n. ° 2: los scripts npm no son lo suficientemente poderosos

Los scripts npm son sorprendentemente poderosos por sí mismos. Hay ganchos previos y posteriores basados ​​en convenciones:

 { "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "prebuild": "echo I run before the build script", "build": "cross-env NODE_ENV=production webpack", "postbuild": "echo I run after the build script" } }

Todo lo que haces es seguir las convenciones. Los scripts anteriores se ejecutarán en orden según su prefijo. El script de compilación previa se ejecutará antes que el de compilación porque tiene el mismo nombre, pero tiene el prefijo "pre". El script postbuild se ejecutará después del script build porque tiene el prefijo "post". Entonces, si creo scripts llamados prebuild, build y postbuild, se ejecutarán automáticamente en ese orden cuando escriba `npm run build`.

También puede descomponer grandes problemas llamando a un script desde otro:

{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "clean": "rimraf ./dist && mkdir dist", "prebuild": "npm run clean", "build": "cross-env NODE_ENV=production webpack" } }

En este ejemplo, la tarea previa a la compilación llama a la tarea de limpieza. Esto le permite descomponer sus scripts en frases de una sola línea, pequeñas, bien nombradas y de responsabilidad única.

Puede llamar a varios scripts en serie en una sola línea usando &&. Los scripts del paso de limpieza anterior se ejecutarán uno tras otro. Esta simplicidad realmente te hará sonreír si eres alguien que ha tenido problemas para obtener una lista de tareas para ejecutar en orden en Gulp.

Y si un comando se vuelve demasiado complicado, siempre puede llamar a un archivo separado:

{ "name": "npm-scripts-example", "version": "1.0.0", "description": "npm scripts example", "scripts": { "build": "node build.js" } }

Estoy llamando a un script separado en la tarea de compilación anterior. Ese script lo ejecutará Node y, por lo tanto, puede utilizar cualquier paquete npm que necesite y utilizar todo el poder de JavaScript en su interior.

Podría continuar, pero las características principales están documentadas aquí. Además, también hay un breve curso de Pluralsight sobre el uso de npm como herramienta de compilación. O echa un vistazo a React Slingshot para ver un ejemplo de todo esto en acción.

Concepto erróneo n. ° 3: las transmisiones de Gulp son necesarias para compilaciones rápidas

Gulp ganó rápidamente tracción sobre Grunt porque las transmisiones en memoria de Gulp son más rápidas que el enfoque basado en archivos de Grunt. Pero no necesitas a Gulp para disfrutar del poder de la transmisión. De hecho, la transmisión siempre se ha integrado en las líneas de comando de Unix y Windows . El operador de tubería (|) transmite la salida de un comando a la entrada de otro comando. Y el operador de redirección (>) redirige la salida a un archivo.

Entonces, por ejemplo, en Unix puedo usar `grep` el contenido de un archivo y redirigir la salida a un nuevo archivo:

grep ‘Cory House’ bigFile.txt > linesThatHaveMyName.txt

El trabajo de arriba se transmite. No se escriben archivos intermedios. (¿Se pregunta cómo ejecutar el comando anterior en una forma multiplataforma? Siga leyendo…)

También puede utilizar el operador `&` para ejecutar dos comandos al mismo tiempo en Unix:

npm run script1.js & npm run script2.js

Los dos scripts anteriores se ejecutarán al mismo tiempo. Para ejecutar scripts de forma simultánea en varias plataformas, use npm-run-all. Esto lleva al siguiente error ...

Concepto erróneo n. ° 4: los scripts npm no se ejecutan en varias plataformas

Muchos proyectos están vinculados a un sistema operativo específico, por lo que las preocupaciones entre plataformas no importan. Pero si necesita ejecutar multiplataforma, los scripts npm aún pueden funcionar muy bien. Innumerables proyectos de código abierto son una prueba. Así es cómo.

La línea de comando de su sistema operativo ejecuta sus scripts npm. Entonces, en Linux y OSX, sus scripts npm se ejecutan en una línea de comandos de Unix. En Windows, los scripts npm se ejecutan en la línea de comandos de Windows. Por lo tanto, si desea que sus scripts de compilación se ejecuten en todas las plataformas, debe hacer felices tanto a Unix como a Windows. Aquí hay tres enfoques:

Enfoque 1: use comandos que se ejecuten en varias plataformas. Hay una sorprendente cantidad de comandos multiplataforma. Aquí hay algunos:

&& chain tasks (Run one task after another)  redirect command output to a file | redirect command output to another command

Enfoque 2: use paquetes de nodos. Puede utilizar paquetes de nodos en lugar de comandos de shell. Por ejemplo, use rimraf en lugar de `rm -rf`. Utilice cross-env para establecer variables de entorno de forma multiplataforma. Busque en Google, npm o libraries.io lo que desea hacer y es casi seguro que haya un paquete de nodo que lo hará multiplataforma. Y si sus llamadas a la línea de comando se vuelven demasiado largas, puede llamar a los paquetes de Node en scripts separados así como también:

node scriptName.js

El script anterior es JavaScript simple y antiguo, ejecutado por Node. Y dado que solo está llamando a un script en la línea de comandos, no está limitado a archivos .js. También puede ejecutar cualquier script que su sistema operativo pueda ejecutar, como Bash, Python, Ruby o Powershell.

Approach 3: Use ShellJS. ShellJS is an npm package that runs Unix commands via Node. So this gives you the power to run Unix commands on all platforms, including Windows.

I used a combination of approach #1 and #2 on React Slingshot.

Pain Point

There are admittedly some downsides: The JSON spec doesn’t support comments, so you can’t add comments in package.json. There are a few ways to work around this limitation:

  1. Write small, well-named, single purpose scripts
  2. Document scripts separately (in a README.md for instance)
  3. Call a separate .js file

I prefer option #1. If you break each script down to have a single responsibility, comments are rarely necessary. The script’s name should fully describe the intent, just like any small well-named function. As I discuss in “Clean Code: Writing Code for Humans”, small single responsibility functions rarely require comments. When I feel a comment is necessary, I use option #3 and move the script to a separate file. This gives me all the compositional power of JavaScript when I need it.

Package.json also doesn’t support variables. This sounds like a big deal, but it’s not for two reasons. First, the most common need for variables revolves around environment, which you can set on the command line. Second, if you need variables for other reasons, you can call a separate .js file. Check out React-starter-kit for an elegant example of this pattern.

Finally, there’s also a risk of creating long, complex command line arguments that are difficult to understand. Code reviews and diligent refactoring are a great way to assure npm scripts are decomposed into small, well-named, single purpose functions that everyone understands. If it’s complex enough to need a comment, you should likely refactor the single script into multiple well named scripts or extract it to a separate file.

Abstractions Must Be Justified

Gulp and Grunt are abstractions over the tools I use. Abstractions are useful, but abstractions have a cost. They leak. They make us dependent upon the plugin maintainers and their documentation. And they add complexity by increasing the number of dependencies. I’ve decided task runners like Gulp and Grunt are abstractions I no longer need.

Looking for details? I walk through how to create a build process using npm scripts from scratch in “Building a JavaScript Development Environment” on Pluralsight.

Comments? Chime in below or on Reddit or Hacker News.

Finally, I’m far from the first person to suggest this. Here are some excellent links:

  • Task automation with npm run — James Holliday
  • Advanced front-end automation with npm scripts — Kate Hudson
  • How to use npm as a build tool — Kieth Cirkel
  • Introduction to npm as a Build Tool — Marcus Hammarberg
  • Gulp is awesome, but do we really need it? — Gonto
  • NPM Scripts for Build Tooling — Andrew Burgess

Cory House is the author of “React and Redux in ES6”, “Clean Code: Writing Code for Humans” and multiple other courses on Pluralsight. He is a Software Architect at VinSolutions and trains software developers internationally on software practices like front-end development and clean coding. Cory is a Microsoft MVP, Telerik Developer Expert, and founder of outlierdeveloper.com.