Si eres un desarrollador de Node.js , entonces estás familiarizado con npm y Yarn . Es posible que incluso tengas una opinión firme sobre el uso de uno en lugar del otro. Durante años, los desarrolladores han tenido problemas con la sobrecarga (en el almacenamiento en disco y el tiempo de compilación) al trabajar con los administradores de paquetes de Node.js, especialmente npm .
Luego llegó pnpm , un administrador de paquetes que maneja el almacenamiento de paquetes de manera diferente, ahorrando espacio a los usuarios y reduciendo el tiempo de compilación. Así es como pnpm describe la diferencia :
“Cuando instalas un paquete, lo guardamos en un almacén global en tu máquina y luego creamos un enlace físico desde él en lugar de copiarlo. Para cada versión de un módulo, solo se guarda una copia en el disco. Al usar npm o yarn, por ejemplo, si tienes 100 paquetes que usan lodash, tendrás 100 copias de lodash en el disco. ¡pnpm te permite ahorrar gigabytes de espacio en disco!”
No es de extrañar que pnpm esté ganando terreno, ya que cada vez más desarrolladores lo están utilizando como su gestor de paquetes preferido. Junto con esa creciente tasa de adopción, muchos desarrolladores que ejecutan sus aplicaciones en Heroku (como yo) querían que pnpm fuera compatible.
Afortunadamente, pnpm está disponible a través de Corepack , que se distribuye con Node.js. Por lo tanto, a partir de mayo de 2024, pnpm ya está disponible en Heroku .
En esta publicación, cubriremos lo que se necesita para comenzar a usar pnpm en Heroku. Además, mostraremos algunos de los beneficios de almacenamiento y tiempo de compilación que se obtienen al usarlo.
pnpm se creó para resolver el problema de almacenamiento redundante e ineficiencias en el manejo de dependencias que existía desde hacía tiempo en el administrador de paquetes Node.js. npm y Yarn copian las dependencias en node_modules
de cada proyecto. Por el contrario, pnpm mantiene todos los paquetes de todos los proyectos en un único almacén global y luego crea vínculos físicos a estos paquetes en lugar de copiarlos. ¿Qué significa esto?
Supongamos que tenemos un proyecto Node.js que utiliza lodash
. Naturalmente, el proyecto tendrá una carpeta node_modules
, junto con una subcarpeta llamada lodash
, llena de archivos. Para ser exactos, [email protected]
tiene 639 archivos y otra subcarpeta llamada fp
, con otros 415 archivos.
¡Eso son más de mil archivos solo para lodash
!
Creé seis proyectos Node.js: dos con pnpm, dos con npm y dos con Yarn. Cada uno de ellos usa lodash
. Echemos un vistazo a la información de solo uno de los archivos en la carpeta de dependencias lodash
.
~/six-projects$ ls -i npm-foo/node_modules/lodash/lodash.js 14754214 -rw-rw-r-- 544098 npm-foo/node_modules/lodash/lodash.js ~/six-projects$ ls -i npm-bar/node_modules/lodash/lodash.js 14757384 -rw-rw-r-- 544098 npm-bar/node_modules/lodash/lodash.js ~/six-projects$ ls -i yarn-foo/node_modules/lodash/lodash.js 14760047 -rw-r--r-- 544098 yarn-foo/node_modules/lodash/lodash.js ~/six-projects$ ls -i yarn-bar/node_modules/lodash/lodash.js 14762739 -rw-r--r-- 544098 yarn-bar/node_modules/lodash/lodash.js ~/six-projects$ ls -i pnpm-foo/node_modules/lodash/lodash.js 15922696 -rw-rw-r-- 544098 pnpm-foo/node_modules/lodash/lodash.js ~/six-projects$ ls -i pnpm-bar/node_modules/lodash/lodash.js 15922696 -rw-rw-r-- 544098 pnpm-bar/node_modules/lodash/lodash.js
El archivo lodash.js
tiene un tamaño de poco más de medio megabyte. No vemos enlaces simbólicos, por lo que, a primera vista, parece que cada proyecto tiene su propia copia de este archivo. Sin embargo, en realidad no es así.
Usé ls
con el indicador -i
para mostrar el inodo del archivo lodash.js
. Puedes ver en los proyectos pnpm-foo
y pnpm-bar
que ambos archivos tienen el mismo inodo ( 15922696
). ¡Están apuntando al mismo archivo! Ese no es el caso de npm o Yarn.
Por lo tanto, si tienes una docena de proyectos que usan npm o Yarn, y esos proyectos usan lodash
, entonces tendrás una docena de copias diferentes de lodash
, junto con copias de otras dependencias en esos proyectos que usan lodash
. En pnpm, cada proyecto y dependencia que requiere esta versión específica de lodash
apunta a la misma copia única y global.
El código de [email protected]
tiene un tamaño de poco menos de 5 MB. ¿Preferirías tener 100 copias redundantes en tu máquina o solo una copia global?
Al final del día, la instalación de dependencias con pnpm es significativamente más rápida, ya que requiere menos espacio en disco y menos recursos. Para los desarrolladores que trabajan en varios proyectos o que administran dependencias en plataformas en la nube, pnpm ofrece una forma más ágil y rápida de administrar paquetes. Esto hace que pnpm sea ideal para un entorno de implementación optimizado como Heroku.
¿Estás listo para comenzar a usarlo? Veamos cómo.
Aquí está la versión de Node.js con la que estamos trabajando en nuestra máquina:
$ node --version v20.18.0
Como mencionamos anteriormente, Corepack viene con Node.js, por lo que simplemente necesitamos usar corepack
para habilitar y usar pnpm. Creamos una carpeta para nuestro proyecto. Luego, ejecutamos estos comandos:
~/project-pnpm$ corepack enable pnpm ~/project-pnpm$ corepack use pnpm@latest Installing [email protected] in the project... Already up to date Done in 494ms
Esto genera un archivo package.json
que se ve así:
{ "packageManager": "[email protected]+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228" }
Esto también genera un archivo pnpm-lock.yaml
.
A continuación, agregamos dependencias a nuestro proyecto. Para fines de demostración, copiamos la lista de dependencies
y devDependencies
que se encuentra en este archivo package.json
de evaluación comparativa en GitHub . Ahora, nuestro archivo package.json
se ve así:
{ "version": "0.0.1", "dependencies": { "animate.less": "^2.2.0", "autoprefixer": "^10.4.17", "babel-core": "^6.26.3", "babel-eslint": "^10.1.0", ... "webpack-split-by-path": "^2.0.0", "whatwg-fetch": "^3.6.20" }, "devDependencies": { "nan-as": "^1.6.1" }, "packageManager": "[email protected]+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228" }
Luego instalamos los paquetes.
~/project-pnpm$ pnpm install
El uso de pnpm es bastante similar al de npm o yarn, por lo que debería resultar intuitivo. A continuación, se incluye una tabla que compara los diferentes usos de los comandos más comunes (extraída de esta publicación ).
Ahora que hemos mostrado cómo poner en marcha un proyecto con pnpm (es bastante sencillo, ¿verdad?), queríamos comparar los tiempos de compilación de diferentes administradores de paquetes cuando se ejecutan en Heroku. Configuramos tres proyectos con dependencias idénticas: usando npm, Yarn y pnpm.
Primero, iniciamos sesión en la CLI de Heroku ( heroku login
).
Luego, creamos una aplicación para un proyecto. Mostraremos los pasos para el proyecto npm.
~/project-npm$ heroku apps:create --stack heroku-24 npm-timing Creating ⬢ npm-timing... done, stack is heroku-24 https://npm-timing-5d4e30a1c656.herokuapp.com/ | https://git.heroku.com/npm-timing.git
Encontramos un paquete de compilación que agrega marcas de tiempo a los pasos de compilación en el registro de Heroku para que podamos calcular los tiempos de compilación reales de nuestros proyectos. Queremos agregar ese paquete de compilación a nuestro proyecto y ejecutarlo antes del paquete de compilación estándar para Node.js. Lo hacemos con los siguientes dos comandos:
~/project-npm$ heroku buildpacks:add \ --index=1 \ https://github.com/edmorley/heroku-buildpack-timestamps.git \ --app pnpm-timing ~/project-npm$ heroku buildpacks:add \ --index=2 heroku/nodejs \ --app npm-timing Buildpack added. Next release on npm-timing will use: 1. https://github.com/edmorley/heroku-buildpack-timestamps.git 2. heroku/nodejs Run git push heroku main to create a new release using these buildpacks.
¡Eso es todo! Luego, enviamos el código para nuestro proyecto administrado por npm.
~/project-npm$ git push heroku main ... remote: Updated 4 paths from 5af8e67 remote: Compressing source files... done. remote: Building source: remote: remote: -----> Building on the Heroku-24 stack remote: -----> Using buildpacks: remote: 1. https://github.com/edmorley/heroku-buildpack-timestamps.git remote: 2. heroku/nodejs remote: -----> Timestamp app detected remote: -----> Node.js app detected ... remote: 2024-10-22 22:31:29 -----> Installing dependencies remote: 2024-10-22 22:31:29 Installing node modules remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 added 1435 packages, and audited 1436 packages in 11s remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 184 packages are looking for funding remote: 2024-10-22 22:31:41 run `npm fund` for details remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 96 vulnerabilities (1 low, 38 moderate, 21 high, 36 critical) remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 To address issues that do not require attention, run: remote: 2024-10-22 22:31:41 npm audit fix remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 To address all issues possible (including breaking changes), run: remote: 2024-10-22 22:31:41 npm audit fix --force remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 Some issues need review, and may require choosing remote: 2024-10-22 22:31:41 a different dependency. remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 Run `npm audit` for details. remote: 2024-10-22 22:31:41 npm notice remote: 2024-10-22 22:31:41 npm notice New minor version of npm available! 10.8.2 -> 10.9.0 remote: 2024-10-22 22:31:41 npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.9.0 remote: 2024-10-22 22:31:41 npm notice To update run: npm install -g [email protected] remote: 2024-10-22 22:31:41 npm notice remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 -----> Build remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 -----> Caching build remote: 2024-10-22 22:31:41 - npm cache remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 -----> Pruning devDependencies remote: 2024-10-22 22:31:44 remote: 2024-10-22 22:31:44 up to date, audited 1435 packages in 4s remote: 2024-10-22 22:31:44 remote: 2024-10-22 22:31:44 184 packages are looking for funding remote: 2024-10-22 22:31:44 run `npm fund` for details remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 96 vulnerabilities (1 low, 38 moderate, 21 high, 36 critical) remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 To address issues that do not require attention, run: remote: 2024-10-22 22:31:45 npm audit fix remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 To address all issues possible (including breaking changes), run: remote: 2024-10-22 22:31:45 npm audit fix --force remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 Some issues need review, and may require choosing remote: 2024-10-22 22:31:45 a different dependency. remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 Run `npm audit` for details. remote: 2024-10-22 22:31:45 npm notice remote: 2024-10-22 22:31:45 npm notice New minor version of npm available! 10.8.2 -> 10.9.0 remote: 2024-10-22 22:31:45 npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.9.0 remote: 2024-10-22 22:31:45 npm notice To update run: npm install -g [email protected] remote: 2024-10-22 22:31:45 npm notice remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 -----> Build succeeded! ...
Observamos el tiempo de los siguientes pasos, hasta que apareció el mensaje Build succeeded
cerca del final:
Installing dependencies
Build
Pruning devDependencies
Caching build
En total, con npm, esta compilación tardó 16 segundos.
Ejecutamos la misma configuración para el proyecto administrado por pnpm, utilizando también el paquete de compilación de tiempos.
~/project-pnpm$ heroku apps:create --stack heroku-24 pnpm-timing ~/project-pnpm$ heroku buildpacks:add \ --index=1 \ https://github.com/edmorley/heroku-buildpack-timestamps.git \ --app pnpm-timing ~/project-pnpm$ heroku buildpacks:add \ --index=2 heroku/nodejs \ --app pnpm-timing ~/project-pnpm$ git push heroku main … remote: 2024-10-22 22:38:34 -----> Installing dependencies remote: 2024-10-22 22:38:34 Running 'pnpm install' with pnpm-lock.yaml … remote: 2024-10-22 22:38:49 remote: 2024-10-22 22:38:49 dependencies: remote: 2024-10-22 22:38:49 + animate.less 2.2.0 remote: 2024-10-22 22:38:49 + autoprefixer 10.4.20 remote: 2024-10-22 22:38:49 + babel-core 6.26.3 … remote: 2024-10-22 22:38:51 -----> Build succeeded!
Para la misma compilación con pnpm, tomó solo 7 segundos.
Descubrimos que el ahorro de tiempo no se produce solo en la instalación inicial. Las compilaciones posteriores, que utilizan la caché de dependencias, también son más rápidas con pnpm.
Cuando comencé a desarrollar Node.js , usé npm. Hace varios años, cambié a Yarn, y eso es lo que había estado usando... hasta hace poco. Ahora, hice el cambio a pnpm. En mi máquina local, puedo liberar espacio de disco sustancial. Las compilaciones también son más rápidas. Y ahora, con el soporte de Heroku para pnpm, esto cierra el ciclo para que pueda usarlo exclusivamente desde el desarrollo local hasta la implementación en la nube.
¡Feliz codificación!