How to delete old node_modules folders on a Mac
Every JavaScript project you clone keeps its own copy of its dependencies in a node_modules folder. A few dozen old projects later, that is a lot of gigabytes you will probably never open again.
Is it safe to delete node_modules?
Yes. The folder is generated from package.json and your lockfile, so npm install, yarn or pnpm install brings it back. What you pay is time and a download. If you are offline, or the project depends on a package version that has since been pulled from the registry, a reinstall can fail. So leave the projects you are working on this week alone and clean up the ones you have not opened in months.
Find the big ones in Terminal
find ~/Projects -name node_modules -type d -prune -exec du -sh {} + | sort -h
Change ~/Projects to wherever you keep code. The -prune flag stops find from walking inside every node_modules folder, which would take a very long time. The largest folders end up at the bottom of the list.
Check when you last touched the project
git -C ~/Projects/old-app log -1 --format=%cd
That prints the date of the last commit. It does not show uncommitted edits, so look at the project folder in Finder as well before you decide it is dead.
Delete them
In Finder, drag the folder to the Trash. That keeps an undo. Running rm -rf node_modules skips the Trash and cannot be undone. For a folder you can regenerate that is usually fine, but it is worth knowing which one you are doing.
Other folders you can treat the same way
Dependencies are not the only generated folders a project leaves behind. Build output and caches are in the same category: .next, dist, build, target (Rust and Maven), .build (Swift packages), Pods, .gradle and Python virtual environments such as venv and .venv. The rule is the same for all of them: if a tool can create it again, it is safe to remove, and it costs you a rebuild.
Doing it with Vervain
Vervain has a module called Project Artifacts that does the searching and the checking. It looks for these folders inside your projects and lists one only when a project file sits next to it, such as package.json, Cargo.toml, Package.swift or Podfile, so it does not guess from the folder name. Results are grouped by project.
Only git-ignored folders of projects that have been idle for 7 days or more are ticked by default. You can change that to 14, 30 or 90 days. Recent git activity counts as activity, including commits, checkouts and uncommitted edits, so a project you worked in yesterday stays unticked even when its files look old. Folders that git does not ignore are never ticked. Everything you remove goes to the Trash, and the History screen can put items back.