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

@rexxars/bundle-stats

v1.7.2

Published

Measure bundle sizes, bundled-with-deps sizes, and import times for package exports

Readme

@rexxars/bundle-stats

Measure bundle sizes, bundled-with-deps sizes, and import times for any npm package's exports.

Generates reports in three formats (terminal, Markdown, JSON) and can compare against a baseline to show deltas — designed for CI workflows that post PR comments with size regressions.

Install

npm install -D @rexxars/bundle-stats

Requires Node.js 24 or later.

CLI Usage

bundle-stats [options]

Options

| Flag | Description | Default | | --------------------- | ----------------------------------------------------------------- | ---------------- | | --package <path> | Path to target package directory or its package.json | . | | --format <fmt> | Output format: cli, markdown, or json | cli | | --compare <path\|-> | Baseline JSON report for delta comparison (- reads stdin) | | | --compare-npm <ver> | Compare against a published npm version (e.g. latest, 5.12.0) | | | --ignore <pattern> | Glob pattern to skip exports (repeatable) | | | --no-benchmark | Skip import time benchmarks | | | --no-bundle | Skip Rollup bundling and treemap generation | | | --outdir <path> | Directory for treemap HTML artifacts | .bundle-stats/ |

Examples

Run a full report on the current directory:

bundle-stats

Report on a specific package, skipping slow steps:

bundle-stats --package packages/sanity --no-bundle --no-benchmark

Generate a JSON baseline, then compare a later run against it:

# save baseline
bundle-stats --format json > baseline.json

# ... make changes, rebuild ...

# compare
bundle-stats --format markdown --compare baseline.json > comment.md

Pipe the baseline via stdin:

cat baseline.json | bundle-stats --format markdown --compare -

Compare against the latest published npm version:

bundle-stats --compare-npm latest --format markdown

Compare against a specific published version:

bundle-stats --compare-npm 5.12.0

Skip specific exports:

bundle-stats --ignore cli --ignore _internal

What It Measures

The tool reads the exports field in your package.json and runs three measurement passes:

| Metric | Description | | ----------------- | --------------------------------------------------------------------------------------------------- | | Internal size | Own source code reachable from the export entry (raw + gzip) | | Bundled size | Rollup bundle with all non-peer dependencies inlined (raw + gzip), plus an interactive treemap HTML | | Import time | Median cold-start import() time in a sandboxed Node.js child process (10 runs, outliers trimmed) |

GitHub Action

The easiest way to use bundle-stats in CI. Add to your workflow:

name: Bundle Stats
on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  bundle-stats:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: 24

      - uses: rexxars/bundle-stats@v1
        with:
          packages: 'sanity, @sanity/vision'
          max-import-time: 500ms
          max-bundle-size-gzip: 100kb

The action automatically checks out the PR base, builds, measures, then does the same for the PR head, and posts a comparison comment on the PR. If thresholds are exceeded, the check fails.

Action Inputs

| Input | Default | Description | | ------------------------ | ----------- | ----------------------------------------------------------------- | | packages | . | Comma-separated package names (resolved via workspaces) or paths | | build-script | build | npm script to run per-package via PM filter syntax | | build-command | | Global build command (overrides per-package builds, for turbo/nx) | | base-ref | PR base SHA | Git ref for baseline measurement | | head-ref | Current SHA | Git ref for current measurement | | max-import-time | | Max import time per export (e.g. 500ms) | | max-bundle-size-gzip | | Max gzip bundle size per export (e.g. 100kb) | | max-bundle-size-raw | | Max raw bundle size per export (e.g. 500kb) | | max-internal-size-gzip | | Max gzip internal size per export (e.g. 50kb) | | max-internal-size-raw | | Max raw internal size per export (e.g. 200kb) | | ignore | | Comma-separated glob patterns to skip exports | | only | | Comma-separated glob patterns for exports to include | | no-benchmark | false | Skip import time benchmarks | | no-bundle | false | Skip Rollup bundling |

Manual CI Usage

For more control, call the CLI directly from your workflow steps:

env:
  BUNDLE_STATS: npx bundle-stats

steps:
  # ... build steps ...

  - name: Generate baseline report
    run: $BUNDLE_STATS --package packages/my-lib --no-benchmark --format json > /tmp/baseline.json

  # ... rebuild after changes ...

  - name: Generate comparison report
    run: |
      cat /tmp/baseline.json | \
        $BUNDLE_STATS --package packages/my-lib --format markdown --compare - \
        > /tmp/comment.md

  - name: Post PR comment
    uses: thollander/actions-comment-pull-request@v3
    with:
      comment-tag: bundle-stats
      file-path: /tmp/comment.md

Library API

The package also exports a programmatic API:

import {generateReport, compareReports, formatMarkdown} from '@rexxars/bundle-stats'

const report = await generateReport({
  packagePath: './packages/my-lib',
  ignorePatterns: ['_internal'],
  noBenchmark: false,
  noBundle: false,
  outdir: '.bundle-stats',
})

// Optional: compare against a previous report
const comparison = compareReports(report, baselineReport)
const markdown = formatMarkdown(report, comparison)

Treemap Viewer

Bundle reports include interactive treemap HTML files (generated by rollup-plugin-visualizer) that show exactly where bundle weight comes from. A hosted viewer at rexxars.github.io/bundle-stats lets you open these treemaps directly from PR comments — no server involved.

The viewer works entirely client-side: the treemap JSON data is gzip-compressed, base64url-encoded, and embedded in the URL fragment (#data=...). Since browsers never send the fragment to the server, the data stays local to your browser. The GitHub Action automatically generates these one-click links in PR comments when the encoded data fits within URL length limits (~1.5 MB). For larger bundles, treemap HTML files are uploaded as CI artifacts instead.

Development

Node 24 runs TypeScript natively, so you can work on the source without a build step:

# Run directly from source — no compilation needed
node bin/bundle-stats.ts --help
node bin/bundle-stats.ts --package /path/to/some-package --no-bundle --no-benchmark

Scripts

pnpm build          # Compile to dist/ with tsup
pnpm check:types    # Type-check with tsc
pnpm lint           # ESLint
pnpm lint:fix       # ESLint with auto-fix
pnpm check:format   # Prettier check
pnpm format         # Prettier write

Build is only needed for publishing to npm — bin/bundle-stats.js imports from dist/, while bin/bundle-stats.ts imports from src/ directly.

License

MIT