npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

onlyfiles

v1.1.0

Published

Fast parallel disk-usage analyser with a local web UI — every folder and every file, biggest first. Show me what you're hiding down there.

Readme


You're at 400 GB used and you have no idea why. du -sh * tells you a folder is big and then abandons you. Disk Utility shrugs and says "Other: 180 GB".

onlyfiles goes through absolutely everything and shows you the lot — every folder, every file, sorted biggest first, with a button next to each one. No subscription, no tipping. It was all sitting on your own hard drive the whole time.

npx onlyfiles

That's it. It scans your home directory, opens your browser, and waits for you to start clicking. Nothing is uploaded anywhere: the server binds to 127.0.0.1 and the only thing that ever leaves your machine is your dignity.

Getting it

Just run it. No install, always the latest:

npx onlyfiles                 # your home directory
npx onlyfiles ~/Library       # somewhere specific
npx onlyfiles / --read-only   # the whole disk, look-don't-touch

Or keep it around, for when the urge strikes again:

npm install -g onlyfiles
onlyfiles

Node 18+. One dependency. No build step. macOS, Linux and Windows.

The six tabs

Tree

Drill down folder by folder. The columns are name, size, share of parent, files and modified — that third one is the one to follow, because taking the biggest share at each level walks you straight to whatever is actually eating the disk.

Big folders load in pages (show N more), and any folder offers list every file here (live re-read) when you want its genuine file-by-file listing instead of the grouped one.

Reclaim candidates

Caches and build artefacts, grouped into categories with a running total, so a hundred scattered node_modules become one line and one decision. Expand a category to see and select the individual folders.

Every category says what it costs to get back, because that is not the same for all of them:

| Category | Why it's a candidate | |---|---| | node_modules | Reinstallable with npm/pnpm/yarn install | | Build output | Regenerated by your build command | | Bundler / tool caches | Regenerated on next run | | AI model weights | Free to delete, but slow and bandwidth-heavy to download again | | Python envs & caches | Recreatable from requirements/lockfile | | Xcode | Xcode rebuilds these; DeviceSupport re-downloads | | Package manager caches | Re-downloaded on demand | | Containers / VMs | Docker & VM disk images — prune from the app | | App caches | Apps regenerate these | | Trash | Already deleted — emptying reclaims the space | | iOS backups | Device backups; often several stale copies |

AI model weights is deliberately its own category instead of being folded in with the caches. Filing 58 GiB of model weights under "regenerated on next run" would be a lie, and a four-hour re-download is a nasty way to find that out.

Biggest files

The largest individual files anywhere in the scan, each with its full path. Where you find the 28 GiB Docker.raw you forgot existed, and that ISO from 2023.

Fullest folders

Ranked by own bytes — what sits directly in each folder, not counting subfolders. Ranking by recursive total just lists your home directory's ancestors; this points at the folder genuinely holding the goods.

File types

Every byte grouped by extension, for the moment you learn that .rlib is 1.4 GiB of your life.

Old & large

Big folders where nothing in the subtree has been modified recently — 6 months to 3 years, your pick. It's judged on the newest file anywhere inside, not the folder's own mtime, so a project full of current work doesn't get flagged merely because its top-level listing hasn't changed. Results are de-nested: one row per decision, rather than the same decision at eight different depths.

There's also a search box across every name in the scan, biggest matches first.

Doing something about it

Every row has four buttons:

| Button | What it does | |---|---| | Show | Highlights it in Finder / Explorer / your file manager, from the outside | | Open | Goes into the folder, or launches the file in whatever app handles it | | Copy | The full absolute path, ready to paste | | | Delete — see below, this bit isn't a joke |

Tick the checkboxes to select several and a bar appears with Show in Finder, Copy paths, Delete… and Clear. Selecting things never collapses or scrolls away anything you had open.

Ticking a folder takes everything inside it, and stays one entry rather than ten thousand. A folder with something ticked underneath it shows a dash instead of a tick, so a selection can never hide under a collapsed branch. Untick one thing inside a ticked folder and the selection is rewritten as "all of it except that" — put the thing back and it folds up into the folder again.

Rows can also be flagged no access (needs Full Disk Access), other volume (not descended into), or N links (a hardlinked file).

The delete part, stated plainly

Jokes off, because this is the part that can actually cost you something.

  • You see the whole list first. The confirmation shows exactly what is going, nested the way it sits on disk, with a size against every line.
  • Move to Trash is the default. Recoverable. The space comes back when you empty it.
  • Delete permanently is a separate, explicit choice, and you have to type DELETE to confirm it.
  • --read-only disables deletion entirely. Show and Open still work.

Before anything is removed, the path must sit strictly inside the folder you scanned, must not be the scan root, must not be a protected location, must survive .. normalisation, and must actually exist. Protected locations match exactly, so ~/Library/Caches stays deletable while ~/Library does not:

/  /System  /Users  /Applications  /usr  /bin  /etc  /var  /private
$HOME  ~/Library  ~/Documents  ~/Desktop  ~/Downloads  ~/Pictures
~/Movies  ~/Music  ~/.ssh  ~/.gnupg  ~/.config

Don't run this under sudo. With sudo and a root of /, ordinary file permissions stop being a backstop and the guards only protect exact paths. Use --read-only for whole-disk scans.

The local API sits behind a random per-run token and a loopback-only Host check. Without those, any web page open in your browser could POST to localhost:4321/api/delete, which is a rude way to learn about CSRF.

Export

↓ Export offers Markdown report, CSV — every entry, Text tree and JSON. Or skip the interface entirely:

onlyfiles ~ --report md   -o disk.md         # summary, candidates, tree
onlyfiles ~ --report csv  -o everything.csv  # one row per entry, for a spreadsheet
onlyfiles ~ --report txt  -o tree.txt        # indented, du-like
onlyfiles ~ --report json -o tree.json

Reports stream, so a full home-directory CSV never has to fit in memory. Mine is 1.2 million rows and it doesn't care.

Changing what it looks at

📂 Change folder — or clicking the path in the header — opens Scan a different folder. A browser can't hand a real filesystem path to a web page, so the picker is driven by the server: navigate subfolders, type an absolute path, or use the shortcuts for Home, Whole disk, Applications, Library, Downloads, and any mounted volume under /Volumes.

show hidden folders is on by default, because .cache, .npm and .ollama are usually the ones worth seeing. The same dialog sets the detail level, and ↻ Rescan re-runs the current folder from scratch.

Scanning / reports the whole Mac, not just the sealed 12 GiB system volume: an APFS volume group presents one device id for both volumes, so the firmlinked /Users is followed normally, while /System/Volumes stays on the skip list so nothing gets counted twice.

External drives have to be their own root, since /Volumes is skipped while scanning /:

onlyfiles /Volumes/Backup

Options

| Flag | What it does | |---|---| | -p, --port <n> | UI port (default 4321, walks upward if taken) | | -m, --min-file-size <s> | Group files below this size per folder. 64k default, 0 itemises everything | | --report <fmt> | md, csv, txt, json — write a report and exit | | -o, --out <file> | Where --report writes (default stdout) | | --depth <n> | Max tree depth in the report | | -c, --concurrency <n> | Worker threads (default: CPUs − 2, capped at 8) | | --cross-device | Also descend into other mounted volumes | | --read-only | Disable deletion | | --no-open | Don't launch a browser |

About --min-file-size

A home directory is mostly tiny files — a couple of million of them, nearly all inside node_modules. Giving each one its own row costs a lot of memory and buys nothing, because nobody reclaims 300 GiB by hunting 8 KiB files.

So by default, files under 64 KiB are folded into a single N smaller files row per folder. Byte totals are completely unaffected — every byte is still counted, just not itemised. And any folder's true listing is one click away via list every file here (live re-read).

Pass --min-file-size 0 to itemise everything up front.

The numbers are real

The unglamorous part, and the reason to trust everything above it.

  • Size on disk, from st_blocks × 512, so sparse files and APFS compression are reflected. Verified byte-exact against du -sk: 12,521,375,719 against 12,521,373,696 on a 12 GiB subtree — a 2 KB gap that is du's own kilobyte rounding.
  • Hardlinks counted once. A file with five names occupies its blocks once. The inode table is a lock-free set in a SharedArrayBuffer shared by every worker thread. With one table per worker, totals drifted by nearly a gigabyte between runs on a pnpm store, because the answer depended on which thread happened to see which copy first.
  • Symlinks are never followed, only counted as their own small entry.
  • Volume boundaries aren't crossed unless you ask.
  • Base-1024 with IEC labels (GiB, not GB). Calling 1024³ a "GB" is how a tool ends up insisting your disk is 460 GB when Finder says 494 GB.

Speed

The walk is bound by lstat latency, not CPU, so it parallelises well. The main thread walks the top few levels to build a frontier of subtree roots and a worker pool drains it — which stops a 200k-file Library from queueing behind a 3-file Documents.

Measured on a 12-core M-series Mac, home directory, 2,664,639 entries / 301 GiB:

| | Time | | |---|---|---| | 8 workers, warm cache | 19.1 s | ~1.0 GB peak RSS | | 1 worker, warm cache | 64.8 s | 3.4× slower | | single-threaded, cold cache | ~5 min | that's the disk, not the code |

Both worker counts report byte-identical totals. That is the entire point of the shared inode table.

Permissions

On macOS some folders are sealed by TCC and can't be read without Full Disk Access. onlyfiles flags those rows no access rather than quietly counting them as empty, and shows the count in the header — around 134 in a typical home directory, 439 when scanning /.

To include them: System Settings → Privacy & Security → Full Disk Access, then add your terminal.

Licence

MIT. Go on then.