node_modules directories — not a virtual filesystem and
not symlinked package contents. That’s a deliberate choice: V8, Vite, and bundlers
resolve through fs.realpath, and symlinked package contents break that. meow
gives you the speed of a shared store with the compatibility of real files.
The layout
Packages are materialized into a project-local content-addressed store undernode_modules/.meow, and the top-level names you import are edges pointing
into it:
Contents are cloned, not linked
The actual package files come from the global cache using the fastest primitive each OS offers:| Platform | Package contents | Dependency edges |
|---|---|---|
| macOS (APFS) | clonefile — O(1) copy-on-write | symlinks |
| Linux | recursive hardlinks | symlinks |
| Windows | recursive hardlinks | NTFS directory junctions |
Why edges stay links but contents don’t. The pointer inside a package’s
node_modules to another package (a graph edge) is a link — cheap, and traversal
through it is fine. But a package’s own files are cloned/hardlinked, never
symlinked, so tools that call fs.realpath (V8, Vite) see a normal directory and
never trip on symlink traversal.Incremental & skippable
meow writes a small.materialized sidecar that records the state of the tree. If
node_modules is already up to date for the current lockfile, materialization is
skipped entirely:
Cleaning & rebuilding
Vendoring
For fully self-contained, reproducible trees (air-gapped builds, committed dependencies, deterministic CI), use vendor mode, which copies packages instead of linking them and normalizes file metadata for byte-reproducibility:| Mode | Contents | Metadata | Use when |
|---|---|---|---|
node_modules (default) | copy-on-write / hardlink | as-is | day-to-day development |
vendor | full copies | normalized (fixed mtimes) | reproducible/air-gapped builds |
Run package binaries
meow x, global installs, and searching the registry.