Introducción a los scripts de NPM

Los scripts de NPM se encuentran entre mis funciones favoritas de NPM. Son sencillos. Reducen la necesidad de herramientas. Por lo tanto, reducen la cantidad de archivos de configuración y otras cosas de las que debe realizar un seguimiento. Y son muy versátiles. También le permiten automatizar muchas tareas comunes. algunos de los cuales se enumeran hacia el final del artículo.

Sin más preámbulos, ¡profundicemos en los scripts de NPM!

¿Qué son los scripts de NPM?

Los scripts de NPM son, bueno, scripts. Usamos scripts para automatizar tareas repetitivas. Por ejemplo, crear su proyecto, minificar archivos de hojas de estilo en cascada (CSS) y JavaScript (JS). Los scripts también se utilizan para eliminar archivos y carpetas temporales, etc. Hay muchas formas de lograr esto: puede escribir scripts bash / batch o usar un ejecutor de tareas como Gulp o Grunt. Sin embargo, mucha gente se está moviendo hacia los scripts de NPM por su simplicidad y versatilidad. También ofrecen la posibilidad de tener menos herramientas para aprender, usar y realizar un seguimiento.

Ahora que tenemos (alguna) idea de qué son los scripts de NPM y qué pueden hacer por nosotros, ¡sigamos adelante y escribamos algunos!

El objeto Scripts en package.json

La mayor parte de nuestro trabajo ocurrirá en el archivo package.json que NPM usa como una especie de manifiesto. Este es el archivo que se crea cuando ejecutanpm init.

Aquí hay un archivo package.json de muestra:

{ "name": "super-cool-package", "version": "1.0.0", "scripts": { ... }, "dependencies": { ... } "devDependencies": { ... } }

Si ha estado trabajando con NodeJS y NPM, estará familiarizado con el archivo package.json. Observe el scriptsobjeto en el archivo. Aquí es donde irán nuestros scripts de NPM. Los scripts de NPM se escriben como pares de clave-valor JSON habituales, donde la clave es el nombre del script y el valor contiene el script que desea ejecutar.

Este es quizás el script NPM más popular (y también es un tipo especial de script):

"scripts": { "start": "node index.js", ...}

Probablemente hayas visto esto muchas veces en tus archivos package.json. Y probablemente sepa que puede escribir npm startpara ejecutar el script. Pero este ejemplo ilustra el primer aspecto importante de los scripts de NPM: son simplemente comandos de terminal. Se ejecutan en el shell del sistema operativo en el que se ejecutan. Entonces podría ser bash para Linux y cmd.exe para Windows.

En este punto, es posible que no esté impresionado. Pero siga leyendo para ver cuán poderosos son realmente los scripts de NPM.

Scripts personalizados

El script que acabamos de ver es uno de los scripts "especiales" de NPM. Puede ejecutarlo simplemente escribiendo npm start. Estos son scripts con nombres que NPM reconoce y les da un significado especial. Por ejemplo, puede escribir un script llamado prepublish. NPM ejecutará el script antes de que su paquete sea empaquetado y publicado, y también cuando lo ejecute npm installlocalmente sin ningún argumento. Más sobre estos scripts aquí.

Pero NPM también le permite definir sus propios scripts personalizados. Aquí es donde el poder de los scripts de NPM comienza a mostrarse.

Veamos un script NPM personalizado súper básico que genera "hola mundo" en la consola. Agregue esto al objeto de scripts de su archivo package.json:

"say-hello": "echo 'Hello World'"

El objeto de scripts en su archivo package.json debería verse así:

..."scripts": { "start": "node index.js", "say-hello": "echo 'Hello World!'"}

Ahora intenta correr npm say-hello. ¿No funciona? Esto se debe a que los scripts personalizados de NPM deben ir precedidos de run-scripto runpara que se ejecuten correctamente. Intente ejecutar npm run-script say-helloo npm run say-hello. La consola dice, "¡Hola mundo!" ¡Hemos escrito nuestro primer script de NPM!

Aquí hay un consejo rápido: para evitar que los registros NPM predeterminados se envíen a la consola cuando ejecuta un script, agregue la --silentmarca. Así es como se vería su comando:

npm run --silent say-hello

Llamar a scripts de NPM dentro de otros scripts de NPM

Una desventaja de usar scripts de NPM aparece cuando su script es bastante complejo (y largo). Este problema se ve agravado por el hecho de que la especificación JSON no admite comentarios. Hay varias formas de solucionar este problema. Una forma es dividir su script en pequeños scripts de un solo propósito y luego llamarlos dentro de otros scripts de NPM. La forma de llamar a un script NPM dentro de otro es sencilla. Modifique su scriptsobjeto para que se vea así:

"scripts": { "say-hello": "echo 'Hello World'", "awesome-npm": "npm run say-hello && echo 'echo NPM is awesome!'"}

Dado que los scripts de NPM se ejecutan en el shell, llamar npm run say-hellodentro de otro script de NPM es casi intuitivo.

Para aquellos de ustedes que no se sientan muy cómodos con los comandos de la terminal, &&en el script se usa para delimitar dos comandos. Por lo tanto, el segundo comando se ejecuta después de la ejecución exitosa del primer comando.

Ahora, cuando se ejecuta npm run awesome-npm, la secuencia de comandos say-hello se ejecuta primero, mostrando "Hello World" a la consola, seguido de la parte de la secuencia de comandos después de &&, que genera "NPM is awesome!"

Aquí hay un caso de uso en el que esto podría ser útil. Suponga que está automatizando el proceso de construcción de su aplicación. Digamos que está usando Webpack como un paquete y su código de distribución va a un directorio llamado “dist”.

Puede comenzar limpiando el directorio. Esto se puede hacer eliminando su contenido o eliminando el directorio en sí y luego haciéndolo nuevamente. Vayamos con el último enfoque. Su comando podría verse así:

rm -r dist && mkdir dist
Tenga en cuenta que esto usa comandos bash. Aprenderá a escribir scripts NPM multiplataforma más adelante en este artículo.

Después de esto, puede invocar el paquete ejecutando el webpackcomando.

Puede ejecutar estos comandos en sucesión utilizando el &&operador. Sin embargo, en aras de la demostración y la modularidad, dividamos esto en dos scripts de NPM que se llaman entre sí.

Así es como se vería el objeto de scripts en este caso de uso:

"scripts": { ... "clean": "rm -r dist && mkdir dist", "build": "npm run clean && webpack"}

¡Ahí tienes! Cómo dividir una tarea más compleja en scripts de NPM más pequeños.

Llamar a los scripts de Shell y Node

A veces, es posible que deba escribir secuencias de comandos mucho más complejas que las que se pueden lograr con 2 o 3 comandos. Cuando surge esta situación, una solución es escribir scripts bash o JS (o scripts en cualquier lenguaje de scripting que desee) y llamarlos desde scripts NPM.

Let’s quickly write a bash script that says hello to you. Create a file called hello.sh in your root directory and paste this code in it:

#!/usr/bin/env bash
# filename: hello.shecho "What's your name?"read nameecho "Hello there, $name!"

It’s a simple script that echoes your name back to you. Now modify the package.json file so that the scripts object has this line of code:

"bash-hello": "bash hello.sh"

Now, when you run npm run bash-hello, it asks you for your name and then says hello to you! Brilliant.

You can do the same thing with JS scripts run using node. An advantage of this approach is that this script will be platform independent since it uses node to run. Here’s a slightly more complex JS script to add two integers received as command line arguments (put this in a file named add.js):

// add.js// adds two integers received as command line arguments
function add(a, b) { return parseInt(a)+parseInt(b);}
if(!process.argv[2] || !process.argv[3]) { console.log('Insufficient number of arguments! Give two numbers please!');}
else { console.log('The sum of', process.argv[2], 'and', process.argv[3], 'is', add(process.argv[2], process.argv[3]));}
El objeto process.argv contiene los argumentos de la línea de comandos dados al script. Los dos primeros elementos, process.argv[0]y process.argv[1], están reservados por nodo. Por lo tanto process.argv[2]y process.argv[3]le permiten acceder a los argumentos de línea de comandos.

Ahora agregue esta línea al scriptsobjeto del package.jsonarchivo:

"js-add": "node add.js"

Finalmente, ejecute el script como un script npm dándole dos números como argumentos de línea de comando:

npm run js-add 2 3

¡Y viola! La salida es

The sum of 2 and 3 is 5

¡Brillante! Ahora somos capaces de escribir scripts mucho más potentes y aprovechar el poder de otros lenguajes de scripting.

Ganchos previos y posteriores

Remember how we talked about a special npm script called prepublish that runs before you publish your package? Such a functionality can be achieved with custom scripts too. We’ve discussed one way to do this in the previous section. We can chain commands using the &&operator, so if you wanted to run script-1 before script-2, you would write:

"script-2": "npm run script-1 && echo 'I am script-2'"

However, this makes our scripts a little dirty. This is because the core functionality of the script is reflected only in the second part of the command (after the && ). One way to write clean scripts is to use pre and post hooks.

Pre and post hooks are exactly what they sound like — they let you execute scripts before and after you call a particular script. All you have to do is define new scripts with the same name as your main script. Yet these are prefixed with “pre” or “post” depending on whether the script is executed before the main script or after it.

Let’s look at our say-hello script again. Say we want to execute the command echo 'I run before say-hello' before say-hello and echo 'I run after say-hello' after say-hello. This is what your scripts object would look like:

"scripts": { "say-hello": "echo 'Hello World'", "presay-hello": "echo 'I run before say-hello'", "postsay-hello": "echo 'I run after say-hello'" }

The “pre” and “post” before the script names tell npm to execute these before and after the script called say-hello respectively.

Now, when you run npm run say-hello, the output of all three scripts shows up in order! How cool is that?

Since all three scripts output to the console and the NPM logs clutter the output, I prefer using the -silent flag while running these. So your command would look like this:

npm run --silent say-hello

And here’s the output:

I run before say-helloHello WorldI run after say-hello

There you have it!

Let’s apply this knowledge to our build script example. Modify your package.json file so that it looks like this:

"scripts": { ... "clean": "rm -r dist && mkdir dist", "prebuild": "npm run clean" "build": "webpack"}

Now our scripts look much cleaner. When you run npm run build, prebuildis called because of the “pre” hook, which calls clean, which cleans up our dist directory for us. Sweet!

Making Our Scripts Cross-Platform

There is one drawback of writing terminal/shell commands in our scripts. This is the fact that shell commands make our scripts platform dependently. This is perhaps what draws our attention to tools like Gulp and Grunt. If your script is written for Unix systems, chances are, it won’t work on Windows, and vice versa.

The first time I used NPM scripts, which called other bash/batch scripts, I thought of writing two scripts per task. One for Unix and one for the Windows command line. This approach may work in use cases where the scripts aren’t that complex and there aren’t many scripts. However, it quickly becomes clear that they are not a good solution to the problem. Some of the reasons behind this are:

  • You have another thing to keep track of that distracts you from your primary task of working on the application. Instead, you end up working in the development environment.
  • You’re writing redundant code — the scripts you write are very similar and accomplish the same task. You’re essentially rewriting code. This violates one of the fundamental principles of coding — DRY: Don’t Repeat Yourself.

So how do we get around this? There are three approaches that you may use:

  1. Use commands that run cross-platform: Many useful commands are common to Unix and Windows. If your scripts are simple, consider using those.
  2. Use node packages: You can use node packages like rimraf or cross-env instead of shell commands. And obviously, you can use these packages in JS files if your script is large and complex.
  3. Use ShellJS: ShellJS es un paquete npm que ejecuta comandos Unix a través de Node. Entonces, esto le da el poder de ejecutar comandos Unix en todas las plataformas, incluido Windows.
Los enfoques anteriores fueron tomados de este brillante artículo de Cory House sobre por qué dejó Grunt y Gulp por scripts de NPM. El artículo detalla muchas cosas que no se tratan en esta serie y concluye con una lista de excelentes recursos. Definitivamente recomendaría que lo lea para comprender mejor los scripts de NPM.

Algunos casos de uso de scripts de NPM

Finalmente, hay muchas cosas que puede hacer con los scripts de NPM. Algunos casos de uso son:

  • Minificación / Uglificación de CSS / JavaScript
  • Automatizar el proceso de construcción
  • Linting su código
  • Comprimir imágenes
  • Inyección automática de cambios con BrowserSync

And a lot more. To learn about how to automate the above-mentioned tasks using NPM scripts, check out this brilliant article on the topic.

Bonus: Commander for Creating CLI Applications Using NodeJS

While we’re discussing NPM scripts and the CLI, I’d like to quickly tell you about a really cool package called commander. Commander allows you to create your own CLI applications. This isn’t really related to NPM scripts, but it’s a cool piece of technology to know. Check out the commander docs here or try one of these tutorials:

  • Build An Interactive Command-Line Application with Node.js — Scotch.io
  • Writing Command Line Applications in NodeJS — freeCodeCamp

Concluding Words

That is all for this article on using NPM scripts. I hope you’ve gained some insight on how you can integrate these into your own projects. This article is by no means an in-depth tutorial on NPM scripts. Hence I’d recommend you learn further both from other resources and from actually using NPM scripts in your own projects.

Also, do connect with me on GitHub and LinkedIn.

Happy Coding! :)