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

vite-plugin-bundler

v0.1.5

Published

A high-performance Vite plugin that compresses build assets using Brotli, Gzip, and Zstandard with worker thread support.

Readme


What's New

  • Zstandard compression (CompressionType.ZSTD) — the only Vite compression plugin with zstd support. Uses native zlib on Node 21.7+ or @mongodb-js/zstd on older versions.
  • Worker threads (useWorkerThreads: true) — offloads CPU-bound compression to a Piscina thread pool for faster large builds.
  • Build report (compressionReport: './stats.json') — writes per-file compression stats to a JSON file for CI dashboards and bundle size tracking.
  • Integrity verification (verifyIntegrity: true) — SHA-256 hash verification of compressed files post-write catches disk corruption and partial writes.
  • Vite 5+ minimum — dropped Vite 4 support; tested against Vite 5, 6, and 7.

Why Compress Build Assets?

Modern web applications ship megabytes of JavaScript, CSS, and HTML. Without compression, users download the full uncompressed payload on every page load. Brotli typically achieves 15-25% better compression than Gzip on web assets, and all modern browsers support it.

| Metric | Without Compression | With Brotli | |--------|:-------------------:|:-----------:| | Typical JS bundle | 500 KB | ~100 KB | | First Contentful Paint | Slower | Faster | | Bandwidth cost | Higher | Lower | | Lighthouse score | Lower | Higher |

This plugin runs after Vite's build step. It reads the output directory, compresses matching files, and writes .br / .gz / .zst variants alongside the originals. Your web server then serves the pre-compressed files directly, avoiding on-the-fly compression overhead.


Features

  • Brotli + Gzip + Zstd: Compress with Brotli, Gzip, Zstandard, or Brotli+Gzip simultaneously
  • Worker Threads: Offload CPU-bound compression to a thread pool via Piscina for faster builds
  • Build Report: Write compression stats to a JSON file for CI dashboards and bundle size tracking
  • Integrity Verification: SHA-256 hash verification of compressed files to catch disk corruption
  • Parallel Processing: Configurable concurrency for fast builds on multi-core machines
  • Smart Filtering: Target specific extensions, file sizes, and glob patterns
  • Compression Threshold: Automatically discard compressed files that don't save enough space
  • Size Budgets: Enforce per-file and total size limits; warn or fail the build when exceeded
  • Progress & Stats Callbacks: Real-time progress reporting and final stats for CI pipelines
  • Retry Logic: Automatic retries with exponential backoff for transient failures
  • Full TypeScript: Exported types, enums, and interfaces with JSDoc

Installation

# npm
npm install --save-dev vite-plugin-bundler

# yarn
yarn add --dev vite-plugin-bundler

# pnpm
pnpm add -D vite-plugin-bundler

Requirements: Node.js >= 18 | Vite >= 5.0.0

Optional dependencies (install only what you need):

# For Zstd compression on Node.js < 21.7 (Node 21.7+ has native zstd in zlib)
npm install @mongodb-js/zstd

# For worker thread support
npm install piscina

Renamed: This package was previously published as vite-plugin-brotli-compress. It has been renamed to vite-plugin-bundler to better reflect its expanded scope — the plugin now supports Brotli, Gzip, and Zstandard (Zstd) compression. If you're upgrading from vite-plugin-brotli-compress, simply update the package name in your package.json and imports:

npm uninstall vite-plugin-brotli-compress
npm install --save-dev vite-plugin-bundler
- import brotliCompress from 'vite-plugin-brotli-compress'
+ import brotliCompress from 'vite-plugin-bundler'

Quick Start

// vite.config.ts
import { defineConfig } from 'vite'
import brotliCompress from 'vite-plugin-bundler'

export default defineConfig({
  plugins: [
    brotliCompress()
  ]
})

That's it. Run vite build and check your dist/ folder for .br files.


Configuration

Full Example

import { defineConfig } from 'vite'
import brotliCompress, { BrotliQuality, CompressionType, GzipLevel } from 'vite-plugin-bundler'

export default defineConfig({
  plugins: [
    brotliCompress({
      // Algorithm
      type: CompressionType.BOTH,
      quality: BrotliQuality.HIGH,
      gzipLevel: GzipLevel.DEFAULT,

      // File selection
      extensions: ['js', 'css', 'html', 'json', 'svg', 'wasm'],
      minSize: 1024,
      maxSize: 10 * 1024 * 1024,
      excludePatterns: ['**/vendor/**'],

      // Threshold — discard if savings < 5%
      compressionThreshold: 0.05,

      // Performance
      parallel: true,
      maxParallel: 10,
      skipExisting: true,
      useWorkerThreads: true,   // offload to thread pool (requires piscina)
      maxWorkerThreads: 4,

      // Build report — write stats JSON for CI dashboards
      compressionReport: './compression-stats.json',

      // Integrity — verify compressed files after write
      verifyIntegrity: true,

      // Size budgets
      budget: {
        maxTotalSize: 500 * 1024,
        maxFileSize: 200 * 1024,
        action: 'warn'
      },

      // Callbacks
      onProgress: ({ percentage, currentFile }) => {
        console.log(`[${percentage}%] ${currentFile}`)
      },
      onComplete: (stats) => {
        console.log(`Saved ${stats.compressionRatio.toFixed(1)}% across ${stats.compressedFiles} files`)
      },

      // Error handling
      continueOnError: true,
      retryAttempts: 2,
      errorCallback: (error, filePath) => {
        console.error(`Failed: ${filePath}`, error.message)
      },

      verbose: true
    })
  ]
})

Options Reference

| Option | Type | Default | Description | |--------|------|---------|-------------| | type | CompressionType | BROTLI | Algorithm: BROTLI, GZIP, ZSTD, or BOTH | | extensions | string[] | ['js','html','css','json','ico','svg','wasm'] | File extensions to compress | | quality | number | 6 | Brotli quality level (0-11) | | gzipLevel | number | 6 | Gzip compression level (0-9) | | zstdLevel | number | 3 | Zstd compression level (1-22) | | minSize | number | 1024 | Minimum file size in bytes | | maxSize | number | undefined | Maximum file size in bytes | | compressionThreshold | number | 0 | Min savings ratio (0-1) to keep compressed file | | deleteOriginal | boolean | false | Delete originals after compression | | shouldCompress | (path, size) => boolean | undefined | Custom filter function | | excludePatterns | string[] | [] | Glob patterns to exclude | | includePatterns | string[] | [] | Glob patterns to include (overrides exclude) | | parallel | boolean | true | Compress files in parallel | | maxParallel | number | 10 | Max concurrent compressions | | useWorkerThreads | boolean | false | Offload compression to worker threads (requires piscina) | | maxWorkerThreads | number | CPU cores | Max number of worker threads | | skipExisting | boolean | false | Skip if .br/.gz/.zst already exists | | compressionReport | string | undefined | File path to write JSON compression report | | verifyIntegrity | boolean | false | SHA-256 hash verify compressed files after write | | continueOnError | boolean | true | Continue if some files fail | | retryAttempts | number | 0 | Retry count for failed compressions | | errorCallback | (error, path) => void | undefined | Called on compression failure | | onProgress | (progress) => void | undefined | Called per-file with progress info | | onComplete | (stats) => void | undefined | Called with final compression stats | | budget | BudgetOptions | undefined | Compressed output size limits | | verbose | boolean | true | Log results to console |

BudgetOptions

| Option | Type | Default | Description | |--------|------|---------|-------------| | maxTotalSize | number | undefined | Max total compressed bytes | | maxFileSize | number | undefined | Max compressed bytes per file | | action | 'warn' \| 'error' | 'warn' | 'error' throws and fails the build |

Enums

// Compression algorithm
enum CompressionType {
  BROTLI = 'brotli',
  GZIP   = 'gzip',
  ZSTD   = 'zstd',
  BOTH   = 'both'   // Brotli + Gzip
}

// Brotli quality presets (0-11)
enum BrotliQuality {
  FASTEST = 0,
  FAST    = 3,
  DEFAULT = 6,
  HIGH    = 9,
  MAXIMUM = 11
}

// Gzip level presets (0-9)
enum GzipLevel {
  NONE    = 0,
  FASTEST = 1,
  FAST    = 3,
  DEFAULT = 6,
  HIGH    = 9,
  MAXIMUM = 9
}

// Zstd level presets (1-22)
enum ZstdLevel {
  FASTEST   = 1,
  FAST      = 3,
  DEFAULT   = 3,
  HIGH      = 9,
  VERY_HIGH = 15,
  MAXIMUM   = 22
}

Quality Guide

| Quality | Speed | Ratio | Best For | |---------|-------|-------|----------| | 0-2 | Very fast | Low | Development / watch mode | | 3-5 | Fast | Medium | CI/CD, frequent deploys | | 6-8 | Balanced | Good | Production (recommended) | | 9-11 | Slow | Excellent | Final release, maximum savings |


Brotli vs Gzip vs Zstd

| | Brotli | Gzip | Zstd | |---|---|---|---| | Compression ratio | ~15-25% better than gzip | Baseline | ~10-20% better than gzip | | Compression speed | Slower at high levels | Fast | Very fast | | Decompression speed | Comparable | Comparable | Faster | | Browser support | All modern browsers | Universal | Cloudflare, nginx, growing | | Best for | Static pre-compressed assets | Broad compatibility fallback | High-throughput / CDN edge | | File extension | .br | .gz | .zst |

Recommendations:

  • Use CompressionType.BOTH (Brotli + Gzip) in production for maximum browser coverage.
  • Use CompressionType.ZSTD if your CDN supports Zstd (e.g. Cloudflare) for the best speed/ratio trade-off.

Examples

React + TypeScript

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import brotliCompress from 'vite-plugin-bundler'

export default defineConfig({
  plugins: [
    react(),
    brotliCompress({
      quality: 9,
      minSize: 2048,
      compressionThreshold: 0.05
    })
  ]
})

Vue

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import brotliCompress, { CompressionType } from 'vite-plugin-bundler'

export default defineConfig({
  plugins: [
    vue(),
    brotliCompress({
      type: CompressionType.BOTH,
      quality: 9,
      gzipLevel: 6
    })
  ]
})

Svelte

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import brotliCompress from 'vite-plugin-bundler'

export default defineConfig({
  plugins: [
    svelte(),
    brotliCompress({ quality: 9 })
  ]
})

Environment-Aware Configuration

brotliCompress({
  // Lower quality for faster dev builds
  quality: process.env.NODE_ENV === 'production' ? 9 : 3,

  // Fewer parallel workers in CI
  maxParallel: process.env.CI ? 4 : 10,

  // Skip tiny files during development
  minSize: process.env.NODE_ENV === 'production' ? 1024 : 10240,

  // Only enforce budgets in CI
  budget: process.env.CI ? {
    maxTotalSize: 500 * 1024,
    action: 'error'
  } : undefined
})

Custom File Filtering

brotliCompress({
  shouldCompress: (filePath, fileSize) => {
    // Skip vendor files
    if (filePath.includes('vendor')) return false

    // Skip already compressed formats
    if (filePath.endsWith('.br') || filePath.endsWith('.gz')) return false

    // Only compress files larger than 2 KB
    return fileSize > 2048
  }
})

Zstd Compression

import brotliCompress, { CompressionType, ZstdLevel } from 'vite-plugin-bundler'

brotliCompress({
  type: CompressionType.ZSTD,
  zstdLevel: ZstdLevel.HIGH, // 1-22, default 3

  // On Node.js 21.7+ native zlib zstd is used automatically.
  // On older Node, install @mongodb-js/zstd:
  //   npm install @mongodb-js/zstd
})

Worker Threads

brotliCompress({
  // Offload compression to a thread pool — ideal for large builds
  useWorkerThreads: true,
  maxWorkerThreads: 4, // defaults to number of CPU cores

  // Requires piscina:
  //   npm install piscina
})

Build Report

brotliCompress({
  // Write a JSON report after compression — great for CI dashboards
  compressionReport: './compression-stats.json',

  // Report includes: per-file sizes, algorithms, savings percentages,
  // totals, compression ratio, and timing
})

The report JSON looks like:

{
  "version": "1.0",
  "timestamp": "2026-05-02T12:00:00.000Z",
  "summary": {
    "totalFiles": 12,
    "compressedFiles": 12,
    "totalOriginalSize": 524288,
    "totalCompressedSize": 104858,
    "compressionRatio": 80.0,
    "timeElapsed": 342
  },
  "files": [
    {
      "filePath": "dist/app.js.br",
      "originalSize": 262144,
      "compressedSize": 52429,
      "algorithm": "brotli",
      "savings": "80.00%"
    }
  ]
}

Integrity Verification

brotliCompress({
  // Verify compressed files via SHA-256 hash after write
  // Catches rare disk corruption or partial writes in CI
  verifyIntegrity: true
})

Compression Threshold

brotliCompress({
  // Discard compressed files that don't save at least 5%
  // Prevents bloat from files that are already compact (e.g. tiny icons)
  compressionThreshold: 0.05,
  verbose: true
})

Progress & Stats Callbacks

brotliCompress({
  // Real-time progress — useful in CI logs
  onProgress: ({ currentFile, currentIndex, totalFiles, percentage }) => {
    console.log(`[${percentage}%] Compressing ${currentFile} (${currentIndex + 1}/${totalFiles})`)
  },

  // Final stats — pipe to dashboards or fail CI
  onComplete: (stats) => {
    console.log(`Compressed ${stats.compressedFiles} files, saved ${stats.compressionRatio.toFixed(1)}%`)
    console.log(`Skipped: ${stats.skippedFiles} | Failed: ${stats.failedFiles}`)
  },

  // Or use compressionReport for automatic JSON output:
  compressionReport: './compression-stats.json'
})

Size Budgets

brotliCompress({
  budget: {
    maxTotalSize: 500 * 1024, // 500 KB total compressed output
    maxFileSize: 200 * 1024,  // 200 KB max per compressed file
    action: 'error'           // fail the build if exceeded
  }
})

Working Example

A complete working example is included in the example/ directory:

cd example
npm install
npm run build
# Check the dist/ folder for .br compressed files

Server Configuration

Pre-compressed files need your web server configured to serve them.

Nginx

# Enable Brotli and Gzip static serving
location ~* \.(js|css|html|json|svg|wasm)$ {
    brotli_static on;
    gzip_static on;

    # For Zstd (requires nginx-mod-zstd or OpenResty):
    # zstd_static on;

    # Fallback for servers without brotli_static module
    # try_files $uri.br $uri.gz $uri =404;
    # add_header Content-Encoding br;
    # add_header Vary Accept-Encoding;
}

Apache

<IfModule mod_rewrite.c>
    RewriteEngine On

    # Serve Brotli if supported
    RewriteCond %{HTTP:Accept-Encoding} br
    RewriteCond %{REQUEST_FILENAME}\.br -f
    RewriteRule ^(.*)$ $1.br [QSA,L]

    # Serve Gzip as fallback
    RewriteCond %{HTTP:Accept-Encoding} gzip
    RewriteCond %{REQUEST_FILENAME}\.gz -f
    RewriteRule ^(.*)$ $1.gz [QSA,L]

    <FilesMatch "\.br$">
        Header set Content-Encoding br
        Header set Vary Accept-Encoding
    </FilesMatch>

    <FilesMatch "\.gz$">
        Header set Content-Encoding gzip
        Header set Vary Accept-Encoding
    </FilesMatch>
</IfModule>

Vercel / Netlify / Cloudflare Pages

These platforms automatically serve Brotli-compressed assets if .br files exist alongside the originals. Cloudflare also supports Zstd (Accept-Encoding: zstd). No extra configuration needed - just deploy your dist/ folder.


How It Works

vite build
    |
    v
[ Vite outputs dist/ ]
    |
    v
[ Plugin scans dist/ for matching files ]
    |
    v
[ Filters by extension, size, patterns, shouldCompress() ]
    |
    v
[ Compresses in parallel — main thread or worker pool ]
    |
    v
[ Applies threshold check — discards if savings too low ]
    |
    v
[ Verifies integrity via SHA-256 (optional) ]
    |
    v
[ Checks budget limits — warns or errors ]
    |
    v
[ Writes compression report JSON (optional) ]
    |
    v
[ Calls onProgress per file, onComplete with final stats ]
    |
    v
[ dist/app.js.br, dist/app.js.gz, dist/app.js.zst ready to serve ]

Testing

# Run all tests
npm test

# Run tests with coverage
npm run test:coverage

# Run with UI
npm run test:ui

# Run a specific suite
npm test -- --grep "integration"
npm test -- --grep "v1.3.0"

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests.

git clone https://github.com/riyajath-ahamed/vite-plugin-bundler.git
cd vite-plugin-bundler
npm install
npm test
npm run build

License

MIT


Changelog

See CHANGELOG.md for a full list of changes.