La estructura de pnpm-lock.yaml
Anatomía del lockfile de pnpm en 2026: lockfileVersion, settings, importers, la separación packages/snapshots, y los campos resolution e integrity. Cómo leerlo con soltura.
Casi nadie lee su pnpm-lock.yaml: lo tratan como un blob generado que ensucia los diffs. Es un error. El lockfile es un documento legible y muy bien diseñado, y saber recorrerlo te da un superpoder: entender por qué una versión concreta acabó en tu árbol, auditar un cambio sospechoso en un pull request, o diagnosticar un conflicto de peerDependencies sin adivinar. En 2026, con pnpm 10 y el formato de lockfile 9.0, la estructura es más limpia que nunca. Vamos a diseccionarla.
- Leer el encabezado:
lockfileVersiony el bloquesettings. - Entender
importerscomo el mapa de tus proyectos delworkspace. - Distinguir
packages(metadatos) desnapshots(el grafo de dependencias). - Interpretar
resolutioneintegritypara rastrear la procedencia de un paquete.
El encabezado: versión y settings
Las primeras líneas fijan el contrato de formato y las opciones que afectan a la resolución:
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
lockfileVersion es crítico: pnpm rechaza (o migra) lockfiles cuyo formato no entiende, y por eso todo el equipo debe usar versiones compatibles de pnpm —se fija con packageManager en package.json y Corepack—. El bloque settings graba las opciones que cambian el árbol resuelto. Se guardan aquí a propósito: si dependieran solo de tu configuración local, dos personas con distinto .npmrc obtendrían árboles distintos, y adiós reproducibilidad.
Los ajustes que verás con más frecuencia:
autoInstallPeers— si pnpm instala automáticamente lospeerDependenciesno satisfechos.excludeLinksFromLockfile— si los enlaces locales se omiten del lockfile.peersSuffixMaxLength— cuánto se abrevia el identificador depeersen las claves (lo veremos abajo).
importers: el mapa de tus proyectos
Aquí es donde el diseño de pnpm brilla frente a otros lockfiles. La sección importers lista cada proyecto del workspace (cada package.json) por su ruta relativa, con sus dependencias declaradas y a qué versión exacta resolvió cada una:
importers:
.:
dependencies:
zod:
specifier: ^3.23.0
version: 3.23.8
packages/ui:
dependencies:
react:
specifier: ^19.0.0
version: 19.1.0
Cada entrada tiene el par clave: specifier (el rango que escribiste) y version (lo que se resolvió). Esa dualidad es el corazón del lockfile: cuando corres una instalación congelada (nivel 6.3), pnpm compara el specifier del lockfile con el rango de tu package.json; si no coinciden, aborta. En un repo de un solo paquete solo verás el importador .; en un monorepo verás uno por cada workspace, lo que convierte a importers en un índice navegable de todo el repo.
packages y snapshots: metadatos frente a grafo
El formato 9.0 separó en dos secciones lo que antes iba mezclado, y entender la división es la clave para leerlo:
packages
Metadatos por versión, independientes del contexto: resolution (procedencia e integridad), engines, los rangos de peerDependencies que declara el paquete. Es la “ficha de identidad” inmutable de cada versión.
snapshots
El grafo real: para cada paquete, a qué versión exacta resolvió cada una de sus dependencias en este árbol. Es donde vive la topología, incluida la resolución concreta de los peers.
packages:
zod@3.23.8:
resolution: {integrity: sha512-Aek05...MJVR7A==}
engines: {node: '>=18'}
snapshots:
zod@3.23.8: {}
'@tanstack/react-query@5.59.0(react@19.1.0)':
dependencies:
'@tanstack/query-core': 5.59.0
react: 19.1.0
Fíjate en la clave @tanstack/react-query@5.59.0(react@19.1.0): ese sufijo entre paréntesis es un peer identifier. Codifica que esta instancia de react-query se resolvió contra react@19.1.0. Si otro rincón del árbol necesitara react-query contra otra versión de React, aparecería una segunda entrada con distinto sufijo. Así pnpm representa, sin ambigüedad, que “el mismo paquete” puede tener grafos distintos según su contexto de peers. La razón de separar packages de snapshots es evitar duplicar los metadatos: la ficha de una versión se escribe una vez, y sus múltiples instanciaciones por contexto de peer viven baratas en snapshots.
resolution e integrity: identidad y verificación
Dentro de packages, el campo resolution responde a “de dónde sale esto y cómo sé que no ha sido alterado”:
# desde el registro npm: hash de contenido
esbuild@0.24.0:
resolution: {integrity: sha512-vF1...Q==}
# desde un tarball directo
mi-lib@1.0.0:
resolution: {tarball: https://ejemplo.dev/mi-lib-1.0.0.tgz}
El integrity es un hash Subresource Integrity del contenido del paquete. En la instalación, pnpm descarga (o toma del store global de contenido direccionable), recalcula el hash y lo compara: si difiere, falla en vez de instalar algo alterado. Rastrear un paquete es, entonces, mecánico:
- Búscalo en
importerspara ver quién lo pide directo y con qué rango. - Búscalo en
snapshotspara ver quién lo arrastra transitivamente y bajo qué contexto depeers. - Búscalo en
packagespara ver su procedencia e integridad.
Si usas catalogs (versiones compartidas de todo el workspace), el lockfile añade además una sección catalogs que graba a qué versión exacta resolvió cada entrada del catálogo: la misma lógica de specifier frente a version, pero elevada al conjunto del monorepo en un único punto de verdad.
flowchart TD A[lockfileVersion y settings] --> B[importers: tus proyectos] B --> C[specifier + version por dependencia] C --> D[snapshots: el grafo resuelto] D --> E[packages: ficha de cada version] E --> F[resolution + integrity] style B fill:#89b4fa,color:#11111b style D fill:#cba6f7,color:#11111b style E fill:#a6e3a1,color:#11111b
Leer el lockfile a mano enseña, pero para el día a día usa las consultas que pnpm ofrece. pnpm why <paquete> te dice quién arrastra una dependencia y en qué versión; pnpm list --depth=Infinity despliega el árbol completo; pnpm licenses list inventaría las licencias del grafo. Contrastar tus deducciones manuales con estas salidas es la mejor forma de comprobar que entiendes de verdad la estructura, en vez de solo reconocerla.
La tentación es tratar el lockfile como ruido: un archivo enorme que hincha los diffs y que nadie revisa. Quien piensa a nivel de sistema hace lo contrario: lo lee como el documento más honesto del repositorio. El package.json expresa intención (rangos, deseos); el lockfile expresa realidad (el grafo exacto que se ejecuta en producción). Cuando un pull request añade una dependencia trivial y el diff del lockfile trae cuatrocientas líneas nuevas, eso no es ruido: es información. Te está diciendo cuántos paquetes transitivos, de cuántos autores distintos, acabas de invitar a tu supply chain. El peer identifier entre paréntesis, la separación packages/snapshots, el hash de integridad: cada pieza existe para que el grafo sea inspeccionable y verificable, no solo instalable. Aprender a leerlo convierte “no sé por qué está esta versión aquí” en una consulta de treinta segundos, y esa fluidez es exactamente lo que distingue a quien depura la cadena de dependencias de quien la sufre.
- Abre tu
pnpm-lock.yamly localiza la secciónimporters: cuenta cuántos proyectos hay y confirma que coinciden con tusworkspace. - Elige una dependencia directa y sigue su rastro:
specifierenimporters, entrada ensnapshots, ficha enpackages. - Encuentra un paquete con un peer identifier entre paréntesis y explica contra qué
peerse resolvió y por qué aparece así. - Ejecuta
pnpm why react(o cualquier paquete tuyo) y contrasta su salida con lo que dedujiste leyendo el lockfile a mano. - Cambia un rango en
package.json, correpnpm instally observa qué líneas del lockfile se movieron: relaciona cada cambio conspecifier,versionosnapshots.