@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-statsRequires 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-statsReport on a specific package, skipping slow steps:
bundle-stats --package packages/sanity --no-bundle --no-benchmarkGenerate 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.mdPipe 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 markdownCompare against a specific published version:
bundle-stats --compare-npm 5.12.0Skip specific exports:
bundle-stats --ignore cli --ignore _internalWhat 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: 100kbThe 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.mdLibrary 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-benchmarkScripts
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 writeBuild 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
