Find and remove orphan node_modules on a Mac
A node_modules directory is reproducible from its project's package.json and one install command. The ones worth removing are orphans: the project is gone, or nothing in it has changed for a long time.
What it is
Everything a compiler or a package manager wrote so it would not have to do the work twice: Xcode DerivedData and Archives, node_modules directories, __pycache__, .gradle, Rust target/ directories, and the virtual disk Docker Desktop stores its images in.
What it is for
It makes the second build faster than the first. On a developer's Mac it is usually the single largest reclaimable category, and none of it is source code: every byte is reproducible from a repository and a build command.
Is it safe to delete?
Yes for the build outputs, which Broza moves to quarantine; the next build recreates them. The Docker virtual disk is the exception: Broza only reports its size and points at docker system prune, because shrinking that file is Docker's job, not a file manager's.
What Broza counts as an orphan
A leaf node_modules directory is orphan when it is not tool-managed and either its parent has no package.json (the project is gone), or none of the project's other entries changed for the --unused-after period (one year by default).
Tool-managed directories are left out: anything under ~/Library (package-manager stores such as pnpm's), under a hidden directory (~/.vscode/extensions, ~/.npm/_npx, ~/.cache) or inside an .app bundle. In monorepos only leaf node_modules are proposed. Orphans are rated REVIEW and moved to quarantine, never deleted outright.
Check it on your Mac
Broza is a free, open-source command-line tool. Every command below is read-only except clean, and clean is a dry run until you add --apply.
broza suggest --category build-cache --unused-after 6m broza clean --category build-cacheNot installed yet? Install Broza with Homebrew, the installer script, or Cargo.