Package Store
How the shared store works and how to maintain it
snpm keeps every downloaded package in a single shared store on disk, and links each project against it. Hardlinks (or copies on Windows) mean every project's node_modules looks like a normal tree, while disk space is shared across every project on the machine.
Layout
The store lives under snpm's data directory:
<data_dir>/
├── packages/ # extracted tarballs
│ ├── react/
│ │ └── 18.2.0/
│ │ ├── package.json
│ │ └── …
│ └── @babel_core/ # `@scope/name` → `@scope_name`
│ └── 7.23.0/…
├── metadata/ # registry metadata cache
├── virtual-store/ # shared .snpm entries (keyed by version + closure)
├── global/ # global installs (snpm add -g)
└── bin/ # global bin shimsA .snpm_complete marker is written when extraction finishes successfully. The next install reads it as a "trust me, this is intact" signal and skips a re-extract.
How a project uses the store
snpm installresolves the dependency graph and writessnpm-lock.yaml.- For each
name@version, snpm ensures<data_dir>/packages/<name>/<version>/is fully extracted. - Each project gets a
node_modules/.snpm/virtual store. Entries inside.snpmeither:- Point at a shared virtual-store entry under
<data_dir>/virtual-store/(most packages). - Stay project-local (patched packages, script-allowed packages, directory-backed
file:deps, packages on the resolver-walk-up compat list).
- Point at a shared virtual-store entry under
- The top-level
node_modules/symlinks point at the appropriate entry in.snpm.
Inspect
snpm store statusSample output:
Store path: /Users/you/Library/Application Support/io.snpm.snpm
Packages: 4128 (1.42 GiB)
Metadata: 872 (38.2 MiB)
Total: 1.46 GiBsnpm store path # print just the store path (good for scripts)Want the runtime config too?
snpm configsnpm config prints all paths plus install settings, registry, auth, allow-scripts, etc.
Maintain
prune
snpm store prune # remove incomplete or orphaned packages
snpm store prune --dry-run # show what would be removedIncomplete packages are those whose extraction never finished (no .snpm_complete marker). Orphans are versions no project references; prune is conservative — it only removes packages that no longer have valid metadata.
clean
snpm clean # interactive: pick what to clean
snpm clean -y # skip confirmation
snpm clean --dry-run # preview
snpm clean --packages # only the package store
snpm clean --metadata # only registry metadata
snpm clean --global # also global installs and bins
snpm clean --all # everythingstore prune is the safer day-to-day choice. clean is the big hammer — useful when you want to reclaim disk space and don't mind re-downloading.
Custom location
Set SNPM_HOME to point cache and data anywhere you want:
export SNPM_HOME=/data/snpmThis gives you /data/snpm/cache and /data/snpm/data. Useful when your home directory is on a small SSD and you want the store on a larger drive.
Local virtual store
Some packages must stay project-local (not pooled across projects). snpm handles this automatically for:
- Patched packages.
- Packages allowed to run lifecycle scripts (
onlyBuiltDependencies,SNPM_ALLOW_SCRIPTS). - Directory-backed
file:dependencies. - Tools that walk up parent directories to find configuration. The default list is
next, nuxt, vite, vitepress, parcel. Override via env var or RC entry — see Configuration.
Everything else pools across projects, so disk usage stays low even on machines with dozens of projects.
Hot-path detection
node_modules/.snpm-integrity is a lockfile-derived hash. On the next install, snpm reads it first; if it still matches, the install completes in tens of milliseconds. The same hash is computed per workspace project so changes in one project don't invalidate the others.
Backups and migration
Moving to a new machine? You can copy <data_dir>/packages and <data_dir>/metadata directly — the store is content-addressed and snpm only ever adds entries. Or skip the copy and let snpm install re-warm it from the registry; cold installs are still parallel and fast.