# weight-check

> **Look before you leap.** Inspect the minified & gzipped cost of any npm dependency _before_ you install it.

`npm install <package>` is a one-way door. By the time you've installed the package, wired it up, built the app, and fired up a bundle visualizer, you've already committed to the bloat. **weight-check** flips that around: it pings the [Bundlephobia](https://bundlephobia.com) API and prints a clean, colorized cost report in your terminal — no install, no build, no visualizer required.

```bash
npx weight-check lodash
```

```
  lodash@4.17.21  ● Moderate
  Lodash modular utilities.

  Minified        68.5 KB
  Minified + gzip 24.7 KB

  Download time (gzipped)
    Slow 3G       493 ms
    Emerging 4G   28 ms

  Dependencies    0
  Tree-shakeable  no ESM build
  Side effects    declared
```

---

## Features

- **Instant size reports** — minified and minified + gzipped sizes for any package.
- **Version-pinned checks** — `weight-check react@18.2.0` measures the exact version you care about.
- **Multi-package mode** — pass several packages and get a compact comparison table plus a grand total.
- **`compare`** — put alternatives head-to-head (`moment` vs `dayjs` vs `date-fns`), sorted lightest-first with a winner and % savings.
- **`scan`** — audit an entire project's `package.json` and see your total install weight, ranked by size, with heavy packages flagged.
- **`scan --recursive`** — point it at a folder and audit **every** project underneath it at once (monorepos included), with a per-project ranking, the heaviest packages across everything, and your total vs. unique dependency footprint.
- **CI budgets** — `--max-size 50kb` exits with code `1` when a package blows your budget, so you can fail a build on bloat.
- **Download-time estimates** — see how long each bundle takes on Slow 3G and Emerging 4G.
- **Tree-shaking signals** — whether the package ships an ESM build and declares side effects.
- **`--json`** — machine-readable output for scripts and dashboards.
- **Local caching** — pinned versions never change, so repeat runs are instant (7-day TTL by default).
- **Zero footprint** — it never installs anything into your project.

## Installation

Run it on demand without installing:

```bash
npx weight-check lodash
```

Or install globally for the shortest possible feedback loop:

```bash
npm install -g weight-check
weight-check lodash      # or the shorthand: wcheck lodash
```

Requires **Node.js 18+** (uses the built-in `fetch`).

## Usage

### Check one or more packages

```bash
weight-check lodash
weight-check react@18.2.0
weight-check react react-dom axios          # comparison table + total
```

### Compare alternatives

```bash
weight-check compare moment dayjs date-fns
# alias:
weight-check vs moment dayjs date-fns
```

```
┌───┬──────────┬──────────┬─────────────┬────────────────────┬─────────┐
│   │ Package  │ Min+Gzip │ vs lightest │ Relative           │ Slow 3G │
├───┼──────────┼──────────┼─────────────┼────────────────────┼─────────┤
│ ★ │ dayjs    │   3.0 KB │    baseline │ █░░░░░░░░░░░░░░░░░ │   59 ms │
│   │ date-fns │  17.1 KB │    +14.1 KB │ ████░░░░░░░░░░░░░░ │  342 ms │
│   │ moment   │  75.4 KB │    +72.4 KB │ ██████████████████ │  1.51 s │
└───┴──────────┴──────────┴─────────────┴────────────────────┴─────────┘

  ★ dayjs is the lightest — 72.4 KB (96%) smaller than moment.
```

### Scan a project's dependencies

```bash
weight-check scan                 # runtime dependencies in ./package.json
weight-check scan --dev           # include devDependencies
weight-check scan --dev-only      # only devDependencies
weight-check scan --project ../my-app
weight-check scan --top 20        # only the first 20 dependencies
```

### Audit *all* your projects at once

Point `scan --recursive` (`-r`) at a parent folder and weight-check discovers every project beneath it (skipping `node_modules`, `dist`, etc.), measures each package **once**, and reports the big picture:

```bash
weight-check scan -r                       # every project under the current folder
weight-check scan -r -p ~/Projects         # every project under ~/Projects
weight-check scan -r --depth 3 --dev       # go 3 levels deep, include devDependencies
```

```
  weight-check scan --recursive  /Users/me/Projects
  10 projects · 28 unique packages

┌────────────────────┬──────┬──────────┬────────────────────┐
│ Project            │ Deps │   Weight │ Share              │
├────────────────────┼──────┼──────────┼────────────────────┤
│ @arch-atlas/core   │    5 │   1.6 MB │ ██████████████████ │
│ @arch-atlas/web    │    4 │ 525.8 KB │ ██████░░░░░░░░░░░░ │
│ …                  │      │          │                    │
└────────────────────┴──────┴──────────┴────────────────────┘

  Heaviest packages (across all projects)
  ts-morph 1.3 MB · lucide-react 421.5 KB · @babel/traverse 190.0 KB · …

  Combined weight    3.0 MB gzipped (counting shared deps once per project)
  Unique footprint   2.4 MB gzipped (22 distinct packages)
  Most shared        chalk — used in 4 projects
```

- **Combined weight** sums each project's total (a shared dep is counted once per project that uses it).
- **Unique footprint** counts every distinct package exactly once — your true dependency surface area.

### Enforce a budget (great for CI)

```bash
weight-check axios --max-size 20kb            # exit 1 if axios > 20 KB gzipped
weight-check scan --max-size 50kb             # fail if ANY dependency > 50 KB
```

The process exits with code `1` when a budget is exceeded (or a package can't be measured), so it drops straight into a CI pipeline:

```yaml
# .github/workflows/bundle-budget.yml
- run: npx weight-check scan --max-size 75kb
```

### JSON output

```bash
weight-check react --json
weight-check scan --json > bundle-report.json
```

## Options

These flags work on `check` (default), `compare`, and `scan`:

| Flag | Description | Default |
| --- | --- | --- |
| `-m, --max-size <size>` | Budget threshold, e.g. `50kb`, `1.5mb`, `2048`. Exits `1` if exceeded. | — |
| `--json` | Emit machine-readable JSON. | `false` |
| `--no-cache` | Bypass the local cache and always hit the network. | — |
| `--bundle-only` | Only use Bundlephobia — skip the npm install-size fallback. | `false` |
| `--ttl <days>` | Cache lifetime in days. | `7` |
| `--timeout <ms>` | Per-request timeout. | `20000` |

`scan`-specific flags:

| Flag | Description |
| --- | --- |
| `-p, --project <path>` | Directory containing `package.json` (default: cwd). |
| `-r, --recursive` | Discover and audit every project under the directory. |
| `--depth <n>` | How deep to search for projects in recursive mode (default: `4`). |
| `--dev` | Also include `devDependencies`. |
| `--dev-only` | Scan only `devDependencies`. |
| `--top <n>` | Limit to the first N dependencies (per project). |

Cache management:

```bash
weight-check cache path      # print the cache file location
weight-check cache clear     # remove all cached sizes
```

## How it reads the numbers

- **Minified + gzip** is the number that actually travels over the wire — it's what weight-check colors and ranks by.
- **Verdicts** are opinionated thresholds on the gzipped size:

  | Verdict | Gzipped size |
  | --- | --- |
  | ● Featherweight | ≤ 5 KB |
  | ● Light | ≤ 20 KB |
  | ● Moderate | ≤ 50 KB |
  | ● Heavy | ≤ 100 KB |
  | ● Massive | > 100 KB |

- **Download times** use effective throughput of ~50 KB/s (Slow 3G) and ~875 KB/s (Emerging 4G), matching Bundlephobia's own profiles.

## Notes & limitations

- **Two size sources.** Web-friendly packages get true **bundle** sizes from [Bundlephobia](https://bundlephobia.com) (minified + gzip). React Native, Expo, native modules, and `@types/*` packages can't be browser-bundled, so weight-check automatically falls back to **npm install size** (unpacked on disk + published tarball). Rows are labelled `bundle` or `install` so you always know which you're looking at.
- Use `--bundle-only` if you want to skip the fallback and only accept Bundlephobia numbers.
- Version ranges in `package.json` (`^1.2.3`) are pinned to their base version (`1.2.3`) when scanning; non-semver specs (`workspace:*`, git URLs, `latest`) fall back to the latest published version.
- Bundle sizes reflect the package on its own — real-world impact depends on your bundler, tree-shaking, and code you actually import. Install sizes are a different (still useful) signal: how heavy the dependency is to download and unpack.

## License

MIT © [phemymii](https://github.com/phemymii)
