A byte counter for your build. Snap before, snap after, see the delta.
⭐ Star this repository if you'd like to support its growth
npm i -D byte-snap
# or
bun add -d byte-snap// vite.config.js
import { snapBuild } from 'byte-snap';
export default {
plugins: [snapBuild.vite({ dir: 'dist' })],
};$ vite build
✓ built in 1.24s
your-package
────────────
264.36 KB → 86.21 KB
saved: 178.15 KB (67.39% smaller)
files: 4 → 2| Option | Default | Description |
|---|---|---|
dir |
'dist' |
Directory to measure |
measureSizeis a deprecated alias ofsnapBuild— same plugin, kept for back-compat.
snapPlugins shows what a single plugin (or group) changed in the final build, by rebuilding
without it and diffing. Drop it into plugins: [...] where that plugin would go:
// vite.config.js
import { snapPlugins } from 'byte-snap';
import compress from 'some-compression-plugin';
export default {
plugins: [snapPlugins([compress])],
};$ vite build
size: plugin some-compression-plugin
────────────
1.91 KB → 338.00 B
saved: 1.58 KB (82.74% smaller)
files: 1 → 1snapPlugins re-runs your build command once (in a child process, without the measured
plugin) to produce the baseline — so the diff is byte-identical except for that plugin's
effect. It defaults to npm run build, which runs your package.json build script
(PM-agnostic, with local bins on PATH). Pass buildCmd for a non-standard command.
| Option | Default | Description |
|---|---|---|
buildCmd |
'npm run build' |
Build command re-run (in a child process) for the baseline. |
savedPercent is always relative to the whole snapshot's beforeBytes — not to some
subsystem within it. If you snapshot a whole bundle to measure one plugin's effect, the
percent shown is "that many bytes off the whole bundle," which can look small even when the
plugin's own footprint shrank a lot. Use savedBytes for the absolute number if that's what
matters.
Two functions. Snapshot, do the work, diff:
import { diff, snap } from 'byte-snap';
const before = snap.path('./dist'); // file or directory (recursive)
await runYourMinifier(); // eg. codemod, image squash, anything
const after = snap.path('./dist');
diff(before, after).print();Want the numbers? .json():
const stats = diff(before, after).json();
// {
// beforeBytes: 1268776, afterBytes: 968496,
// savedBytes: 300280, savedPercent: 23.67,
// beforeFiles: 34, afterFiles: 34, fileDelta: 0
// }Fail build if a custom plugin didn't save enough
const before = snap.path('./dist');
await runYourMinifier();
const { savedBytes } = diff(before, snap.path('./dist')).json();
// expect at least 100 bytes saved
if (savedBytes < 100) {
console.error(`Baseline not met. Saved only ${savedBytes} bytes 🚨`);
process.exit(1);
}Compare gzip vs brotli on a single file
import { diff, snap } from 'byte-snap';
import { readFileSync } from 'node:fs';
import { brotliCompressSync, gzipSync } from 'node:zlib';
const raw = readFileSync('dist/index.js');
diff(snap.buffer(raw), snap.buffer(gzipSync(raw))).print();
diff(snap.buffer(raw), snap.buffer(brotliCompressSync(raw))).print();Measure a raw string transform
diff(snap.text(source), snap.text(minify(source))).print();| Call | Returns / does |
|---|---|
snap.path(target) |
Snapshot a file or directory (recursive). Missing path → empty. |
snap.text(str) |
Snapshot a string's UTF-8 byte length. |
snap.buffer(buf) |
Snapshot a Buffer or ArrayBuffer. |
diff(a, b) |
Compare two snapshots → { print(title?), json() }. |
snapBuild.vite() |
Whole-build plugin (and .rollup, .webpack, .esbuild, …). |
snapPlugins([p], o) |
Plugin array measuring what plugin p changed (o.buildCmd). |
Each snapshot also exposes per-file detail: { files, bytes: { total, average, largest, smallest }, entries }.
MIT © jayF0x
