Qué resuelve un gestor de paquetes#
Ya conoces el ciclo básico: npm install descarga las dependencias declaradas en package.json y las vuelca en node_modules/. Lo que quizás no está tan claro es qué pasa exactamente ahí dentro.
Un gestor de paquetes hace tres cosas a la vez:
- Registro. Sabe dónde está cada paquete. npm usa
registry.npmjs.orgpor defecto: el catálogo público de más de dos millones de paquetes. - Instalación. Descarga el código que pides, pero también el código que ese código necesita, y el código que ese código necesita, y así sucesivamente.
- Declaración. Anota en
package.jsonqué instalaste y con qué rango de versiones, para que cualquiera que clone el proyecto pueda reproducir lo mismo.
# Instala un paquete y lo anota en "dependencies" de package.json.
npm install date-fns
# Instala una herramienta de desarrollo y la anota en "devDependencies".
# La flag -D es la abreviatura de --save-dev.
npm install -D vitestLa diferencia entre dependencies y devDependencies importa: las primeras acaban en el bundle que descarga el usuario; las segundas solo se usan mientras programas. Vite, los linters y los frameworks de tests son siempre devDependencies.
El árbol de dependencias#
Cuando instalas date-fns, no solo descargas date-fns. date-fns puede necesitar otros paquetes, que a su vez necesitan otros. Todo eso forma el árbol de dependencias:
tu proyecto
├── date-fns ← dependencia directa (la que pediste)
│ └── (ninguna) ← date-fns no tiene dependencias propias
└── vitest ← dependencia directa de desarrollo
├── @vitest/runner ← dependencia TRANSITIVA (la pediste vitest, no esta)
├── vite-node ← dependencia transitiva
└── why-is-node-running ← dependencia transitivaLas dependencias transitivas son las de tus dependencias: no las pediste tú directamente, pero el gestor las instala porque algo que sí pediste las necesita. Por eso node_modules/ puede contener cientos de carpetas aunque tu package.json solo declare cinco o seis paquetes.
npm a fondo#
Instalar, actualizar y eliminar#
# Instala todos los paquetes declarados en package.json (el comando de entrada al proyecto).
npm install
# Instala un paquete concreto y actualiza package.json y el lockfile.
npm install date-fns
# Instala como herramienta de desarrollo (va a devDependencies, no al bundle de producción).
npm install -D vitest
# Desinstala un paquete y lo elimina de package.json.
npm uninstall date-fns
# Actualiza un paquete a la versión más reciente que permite el rango de package.json.
npm update date-fnsScripts#
# Ejecuta el script "dev" declarado en package.json → normalmente arranca el dev server.
npm run dev
# Ejecuta el script "build" → genera la versión de producción en dist/.
npm run build
# Lista todos los scripts disponibles en el proyecto.
npm runLos scripts son atajos. En vez de recordar vite --host --port 3000, escribes npm run dev y ya.
npx: ejecutar sin instalar#
# Descarga create-vite, andamia un proyecto nuevo y lo descarta.
# No instala create-vite de forma permanente: lo usas una vez y ya está.
npx create-vite mi-proyecto
# Otro ejemplo: ejecuta la versión más reciente de prettier sobre un fichero,
# sin tener prettier instalado globalmente.
npx prettier --write src/index.jsnpx es especialmente útil para herramientas que usas una vez (andamiadores) o de forma muy esporádica. No tiene sentido instalarlas de forma permanente y mantenerlas actualizadas.
El lockfile: reproducibilidad garantizada#
Cuando haces npm install date-fns, tu package.json anota algo como "date-fns": "^4.1.0". El acento circunflejo ^ significa “cualquier versión compatible con 4.x”: si mañana sale 4.2.0, alguien que clone el proyecto e instale podría obtener esa versión más nueva.
El lockfile (package-lock.json) resuelve ese problema:
{
"name": "team-builder",
"lockfileVersion": 3,
"packages": {
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-..."
}
}
}El lockfile fija la versión exacta de cada paquete (incluidas las transitivas) y su hash de integridad. Esto significa que si tú tienes date-fns@4.1.0 y un compañero clona el repo e instala, también obtendrá date-fns@4.1.0. Siempre.
Sin embargo, tener lockfile no basta por sí solo: npm install puede actualizar versiones dentro del rango semver y reescribir el lockfile sin que te des cuenta. Por eso existe npm ci: reproduce el árbol exacto del lockfile sin tocarlo, y falla si hay cualquier divergencia. Es el comando que se usa en entornos limpios y en la integración continua (CI: un servidor que, en cada cambio que subes al repositorio, descarga el proyecto desde cero y lo construye y prueba de forma automática; lo verás a fondo más adelante).
El lockfile se sube a Git. Es parte del proyecto.
npm ci: instalaciones reproducibles#
# Borra node_modules/ y reinstala EXACTO lo del lockfile.
# No actualiza versiones. Falla si package.json y lockfile no concuerdan.
# Es lo que ejecuta la integración continua (CI) para garantizar reproducibilidad.
npm ciLa diferencia con npm install:
| Comando | Actualiza lockfile | Falla si hay divergencia | Uso |
|---|---|---|---|
npm install | Sí, puede | No | Desarrollo diario |
npm ci | No | Sí | CI/CD, entornos limpios |
Cuando alguien incorpora cambios al proyecto, lo correcto es npm ci, no npm install: garantiza que trabajas con el árbol exacto que el equipo validó.
pnpm: el store global#
pnpm resuelve un problema práctico de npm: si tienes diez proyectos y todos usan React, npm descarga y guarda diez copias de React (una en cada node_modules/). pnpm guarda una sola vez cada versión de cada paquete en un store global y crea enlaces hacia cada proyecto.
Store global (en tu carpeta de usuario)
└── react@19.2.7/ ← guardado una vez
└── ...
tu-proyecto/node_modules/react → enlace al store
otro-proyecto/node_modules/react → el MISMO enlace al storeEl resultado:
- Menos disco. Sin duplicados.
- Instalaciones más rápidas. Si el paquete ya está en el store, no lo descarga.
- node_modules estricto. npm aplana el árbol de paquetes (hoisting): sube a la raíz de
node_modules/los paquetes de tus dependencias, lo que los deja accesibles en tu código aunque no los hayas declarado tú. Si esa dependencia desaparece o cambia de versión, tu código se rompe en producción sin que hayas tocado nada: son las llamadas phantom dependencies. pnpm no aplana el árbol; solo expone ennode_modules/lo que tú declaraste, y por eso elimina ese problema.
pnpm usa su propio lockfile: pnpm-lock.yaml.
# Con pnpm, los comandos principales son equivalentes a npm.
# instala todo lo de package.json (sin nombre de paquete)
pnpm install
# instala un paquete concreto y lo anota en dependencies
pnpm add date-fns
# herramienta de desarrollo: va a devDependencies
pnpm add -D vitest
# ejecuta el script dev (o simplemente: pnpm dev)
pnpm run devEste curso usa pnpm. Cuando ves pnpm en los comandos de los ejercicios, ya sabes por qué.
yarn: existe y lo verás#
yarn fue creado por Meta (entonces Facebook) en 2016 para resolver problemas de velocidad y reproducibilidad que npm tenía entonces. npm los resolvió después.
Hay dos versiones muy distintas:
- yarn classic (v1): La que verás en proyectos existentes. Muy similar a npm. Su lockfile es
yarn.lock. - yarn Berry (v2+): Reescritura moderna con Plug’n’Play (PnP), que elimina
node_modules/. Cambio grande; no es compatible con v1.
# Instala todo lo declarado en package.json (sin nombre de paquete).
yarn
# Instala un paquete concreto y lo anota en dependencies.
yarn add date-fns
# Instala como herramienta de desarrollo (va a devDependencies).
yarn add -D vitest
# Reinstalación limpia: equivalente a npm ci, no actualiza el lockfile.
yarn install --frozen-lockfileNo insistiremos en yarn. Lo mencionamos porque lo encontrarás en proyectos heredados y necesitas saber qué es cuando lo ves. Para proyectos nuevos, el peso de 2026 está en npm y pnpm.
La misma idea, tres comandos#
La tabla de equivalencias que más usarás:
| Acción | npm | pnpm | yarn (classic) |
|---|---|---|---|
| Instalar todo | npm install | pnpm install | yarn |
| Instalar paquete | npm install X | pnpm add X | yarn add X |
| Instalar dev | npm install -D X | pnpm add -D X | yarn add -D X |
| Ejecutar script | npm run X | pnpm X | yarn X |
| Reinstalación limpia | npm ci | pnpm install --frozen-lockfile | yarn install --frozen-lockfile |
La flag --frozen-lockfile es el equivalente de pnpm y yarn a npm ci: no actualiza el lockfile y falla si no concuerda con package.json.
corepack: fijar el gestor#
Imagina que tu proyecto usa pnpm, pero alguien del equipo no lo sabe y ejecuta npm install. Los lockfiles divergen. El árbol difiere. Aparecen errores difíciles de reproducir.
corepack evita eso: es una herramienta que viene con Node y que, una vez activada, lee el campo packageManager de tu package.json y fuerza que todos usen ese gestor y esa versión.
# Activa corepack en tu máquina (solo una vez).
# Según tu versión de Node puede que antes necesites: npm i -g corepack
corepack enableLuego, en tu package.json:
{
"name": "team-builder",
"packageManager": "pnpm@11.7.0"
}La versión se fija a propósito: la gracia de packageManager es que todo el equipo use exactamente esa versión, no “la que tengan instalada”. Pon la tuya real (mírala con pnpm --version) en lugar de 11.7.0.
A partir de ahí, si alguien intenta npm install en ese proyecto, corepack lo detiene y le indica que debe usar pnpm. Todo el equipo, mismo gestor, misma versión. Sin fricción.
Comprueba lo que sabes#
Pregunta 1 de 5
¿Para qué sirve el lockfile?
Tu turno#
Este ejercicio se hace en local: toma el Team Builder con Vite que montaste en el Nivel 3 y practica el ciclo completo. Instala dependencias, trabaja con el lockfile, crea un script propio y, si te animas, fija el gestor para que todo el equipo trabaje igual. Cuando termines, despliega las soluciones y compara los comandos.
Ejercicio · hazlo en local
Mueve tu Team Builder al ciclo real de un proyecto
Sobre el Team Builder con Vite que montaste en el Nivel 3, practica el ciclo completo de un proyecto profesional: instalar dependencias distinguiendo su tipo, usar el lockfile, crear scripts propios y, si llegas al nivel excelente, fijar el gestor para que todo el equipo trabaje igual.
Paso 1: Que funcione
- Instalas `date-fns` como dependencia de producción (`npm install date-fns`).
- Instalas `vitest` como herramienta de desarrollo (`npm install -D vitest`).
- Abres el lockfile (`package-lock.json`) y localizas las versiones exactas de ambos paquetes.
- Ejecutas `npm run dev` y el proyecto sigue funcionando.
Paso 2: Que sea reproducible
- Borras `node_modules/` y reinstalas con `npm ci` (no `npm install`).
- Añades en `package.json` el script encadenado `"check": "npm run lint && npm run build"`.
- Ejecutas `npm run check` y compruebas que corre los dos pasos en orden.
Paso 3: Que el equipo use lo mismo
- Activas corepack en tu máquina (`corepack enable`; si tu versión de Node lo pide, antes: `npm i -g corepack`).
- Añades `"packageManager": "pnpm@11.7.0"` en tu `package.json`.
- Instalas con `pnpm install` y arrancas con `pnpm dev`. El proyecto sigue corriendo.
Cómo hacerlo en local
Clona el repositorio del curso, entra en la carpeta del ejercicio y abre el
index.html en tu navegador. Toda tu solución va en
solucion.js.
git clone <repo>
cd exercises/nivel-4/package-managers
# abre index.html en el navegador y edita solucion.js Ver soluciones
# OK — instalar y distinguir dependencias de herramientas, sobre el Team Builder.
# Una dependencia de PRODUCCIÓN: date-fns va a "dependencies" en package.json.
# Esto significa que entrará en el bundle que descarga el usuario final.
# Cualquier librería que tu app necesite para funcionar en el navegador va aquí.
npm install date-fns
# Una herramienta de desarrollo: vitest va a "devDependencies" con la flag -D.
# Los tests solo los ejecutas tú mientras programas; el usuario nunca los ve.
# Vitest, los linters y Vite son siempre -D: nunca deben llegar a producción.
npm install -D vitest
# El lockfile (package-lock.json) ya fija las versiones exactas: ábrelo y míralo.
# Ejecuta un script declarado en package.json (atajo, no recuerdas el comando largo).
npm run dev Por qué este nivel
- Distingues una dependencia de producción (date-fns) de una de desarrollo (-D, vitest): solo la primera acaba en el bundle del usuario.
- El lockfile ya fija las versiones exactas; no tienes que pensar en ello.
- Los scripts son atajos: npm run dev en vez de recordar el comando largo.
# Mejor — instalaciones reproducibles y un script propio.
# Borra node_modules y reinstala EXACTO lo del lockfile. Es lo que corre la CI:
# no actualiza versiones, reproduce el árbol bit a bit. Falla si package.json y
# lockfile no concuerdan (red de seguridad).
rm -rf node_modules
npm ci
# Añade a package.json un script encadenado (lo escribes en "scripts"):
# "check": "npm run lint && npm run build"
# y lánzalo como uno solo:
npm run check Por qué es mejor que el anterior
- npm ci reproduce el árbol bit a bit desde el lockfile: es lo que hace que 'en mi máquina funciona' deje de ser un problema.
- Falla si package.json y lockfile no concuerdan: red de seguridad antes de desplegar.
- Automatizar lint && build en un solo comando es el embrión de una pipeline de CI: cuando llegues a GitHub Actions harás exactamente esto, pero en el servidor. Que funcione aquí primero.
# Excelente — fijar el gestor y migrar a pnpm.
# Activa corepack (según tu versión de Node puede pedir: npm i -g corepack).
corepack enable
# Declara el gestor del proyecto en package.json (campo packageManager):
# "packageManager": "pnpm@9.0.0"
# La versión se fija a propósito: que todo el equipo use EXACTAMENTE esta versión.
# Sustituye 9.0.0 por la tuya real (pnpm --version).
# Ahora todo el equipo usa el MISMO gestor y versión: si alguien ejecuta npm install,
# corepack lo detiene y le indica que debe usar pnpm.
# Instala con pnpm: guarda cada paquete una vez en el store global y lo ENLAZA.
# Menos disco, más velocidad, y node_modules estricto (no usas lo que no declaraste).
pnpm install
pnpm dev Por qué es mejor que el anterior
- corepack + packageManager fijan el gestor y su versión para TODO el equipo: nadie instala lo que no toca.
- pnpm guarda cada paquete una vez en el store y lo enlaza: menos disco, más velocidad.
- Su node_modules estricto te impide usar paquetes que no declaraste: bugs que aparecen solo en producción, evitados.