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

squeezit

v1.17.0

Published

Lossless image optimizer CLI, JS/TS API, and bundler plugins for PNG, JPEG, GIF, WebP, SVG, AVIF, HEIC, JXL, ICO, BMP, and more.

Readme

squeezit is a CLI for aggressively compressing images without casually degrading them. It is designed for codebases, asset folders, and content repositories where you want smaller files, predictable behavior, and a command you can trust in day-to-day workflows.

It supports direct file paths, shell-style patterns like *.png, glob expressions like images/**/*.webp, and a no-argument mode that scans supported image files in the current directory. Recursive scanning is available when you ask for it.

Why Squeezit

  • Lossless-first workflow across common web and design formats
  • APNG, JXL, and ICO support alongside common web asset formats
  • Friendly CLI output with clear summaries, skips, and failures
  • Safe threshold-based replacement so tiny wins do not churn your files
  • Pattern matching that works with both regular shell parameters and glob expressions
  • Works well as a local cleanup tool before commits or releases
  • Published for Node users, while Bun remains the development and build toolchain

Installation

npm

npm install -g squeezit

bun

bun add -g squeezit

After installation, both commands are available:

squeezit --help
sqz --help

Quick Start

Compress supported images in the current directory:

squeezit

Preview changes without modifying files:

squeezit -d

Strip metadata only, without recompressing:

squeezit --exif

Target only top-level PNGs:

squeezit "*.png"

Target nested files with glob expressions:

squeezit -r "images/**/*.{png,jpg,webp}"

Optimize an icon container in dry-run mode:

squeezit favicon.ico -d

Run the shorter alias:

sqz -r assets/**/*.jpg -d

Check for a newer published version:

squeezit --check-update

Self-update to the latest release:

squeezit -U

Integrations

squeezit is now structured to expose first-party integrations from the same package.

Available today:

  • Root JS/TS API via import { optimizeFile, optimizeFiles, stripMetadata } from "squeezit"
  • Gulp plugin via import { squeezitGulp } from "squeezit/gulp"
  • Grunt plugin via const { registerSqueezitTask } = require("squeezit/grunt")
  • Vite plugin via import { squeezitVite } from "squeezit/vite"
  • Webpack plugin via import { squeezitWebpack } from "squeezit/webpack"
  • Rollup plugin via import { squeezitRollup } from "squeezit/rollup"
  • Parcel optimizer plugin with implementation exported at squeezit/parcel
  • Astro wrapper via import { squeezitAstro } from "squeezit/astro"
  • Next.js wrapper via import { withSqueezit } from "squeezit/next"
  • esbuild plugin via import { squeezitEsbuild } from "squeezit/esbuild"
  • Babel plugin via import { squeezitBabel } from "squeezit/babel"

The supported programmatic integration surfaces are the root JS/TS API, the Gulp plugin, the Grunt plugin, the Vite plugin, the Webpack plugin, the Rollup plugin, the Parcel optimizer plugin, the Astro wrapper, the Next.js wrapper, the esbuild plugin, and the Babel plugin.

Gulp

const { src, dest } = require("gulp");
const { squeezitGulp } = require("squeezit/gulp");

exports.images = function images() {
  return src("assets/**/*").pipe(squeezitGulp()).pipe(dest("dist/assets"));
};

The Gulp plugin runs as a Vinyl transform, uses the default compression strategy, and always enables metadata stripping. It supports buffered Vinyl files, stream-backed Vinyl files by buffering them internally before optimization, and path-backed null Vinyl files when file.path is available. Null Vinyl files without a usable path pass through unchanged. It does not expose or use max mode.

Grunt

const { registerSqueezitTask } = require("squeezit/grunt");

module.exports = function (grunt) {
  registerSqueezitTask(grunt);

  grunt.initConfig({
    squeezit: {
      images: {
        files: [
          {
            src: ["assets/**/*.{png,jpg,webp,svg}"],
            dest: "dist/assets",
          },
        ],
      },
    },
  });
};

The Grunt plugin registers a real multi-task, uses the default compression strategy, and always enables metadata stripping. v1 respects Grunt file mappings, so it can optimize files in place or write optimized output to mapped destination paths. It does not expose or use max mode.

Vite

import { defineConfig } from "vite";
import { squeezitVite } from "squeezit/vite";

export default defineConfig({
  plugins: [squeezitVite()],
});

The Vite plugin runs only for production builds, optimizes emitted assets from the output directory, uses the default compression strategy, and always enables metadata stripping. It does not expose or use max mode.

Webpack

const { squeezitWebpack } = require("squeezit/webpack");

module.exports = {
  plugins: [squeezitWebpack()],
};

The Webpack plugin runs after assets are written to the configured output directory, optimizes emitted image files from that directory, uses the default compression strategy, and always enables metadata stripping. It does not expose or use max mode.

Rollup

import { defineConfig } from "rollup";
import { squeezitRollup } from "squeezit/rollup";

export default defineConfig({
  input: "src/index.js",
  output: {
    dir: "dist",
    format: "esm",
  },
  plugins: [squeezitRollup()],
});

The Rollup plugin optimizes emitted image assets with the default compression strategy and always enables metadata stripping. It prefers in-memory asset optimization during bundle generation, including hash-safe filename/reference updates when asset bytes change, and uses a post-write output pass as a fallback for emitted files not already handled in memory. It does not expose or use max mode.

Parcel

squeezit/parcel exports the Parcel optimizer implementation, but Parcel itself only accepts optimizer specifiers that match its parcel-optimizer-* naming convention. When consuming it from the main squeezit package, point .parcelrc at the built plugin file inside node_modules:

.parcelrc

{
  "extends": "@parcel/config-default",
  "transformers": {
    "url:*": ["@parcel/transformer-raw"]
  },
  "optimizers": {
    "*.{png,gif,webp,svg,heif,heic,avif,bmp,ico,cur,jxl}": [
      "...",
      "./node_modules/squeezit/dist/parcel.cjs"
    ]
  }
}

package.json

{
  "squeezit": {
    "parcel": {
      "enabled": true,
      "checkDependencies": true,
      "productionOnly": true
    }
  }
}

The Parcel integration is a real Parcel optimizer plugin. It runs in Parcel's asset pipeline, uses the default compression strategy, always enables metadata stripping, and is production-only by default. Its small config surface is read from package.json under squeezit.parcel, and it does not expose or use max mode.

Astro

import { defineConfig } from "astro/config";
import { squeezitAstro } from "squeezit/astro";

export default defineConfig({
  output: "static",
  integrations: [squeezitAstro()],
});

The Astro wrapper is a thin integration over squeezit/vite. It only matters for astro build, targets static Astro output in v1, uses the default compression strategy, and always enables metadata stripping. SSR and hybrid Astro output are not supported by this wrapper yet, and it does not expose or use max mode.

Next.js

const { withSqueezit } = require("squeezit/next");

module.exports = withSqueezit({
  webpack(config) {
    return config;
  },
});

The Next.js wrapper augments webpack-based Next builds by injecting the squeezit Webpack plugin through next.config.js/next.config.ts. It uses the default compression strategy, always enables metadata stripping, and does not expose or use max mode.

Turbopack support is not included in this wrapper yet. It is planned and coming soon, but this integration currently targets Next’s webpack build pipeline only.

esbuild

import { build } from "esbuild";
import { squeezitEsbuild } from "squeezit/esbuild";

await build({
  entryPoints: ["src/index.ts"],
  outdir: "dist",
  bundle: true,
  plugins: [squeezitEsbuild()],
});

The esbuild plugin runs after a successful disk-backed build, optimizes emitted image files from the written output directory, uses the default compression strategy, and always enables metadata stripping. It does not expose or use max mode.

Babel

import { squeezitBabel } from "squeezit/babel";

export default {
  plugins: [[squeezitBabel, { productionOnly: true }]],
};

The Babel plugin is narrower than the emitted-asset integrations. It runs at compile time, is production-only by default, rewrites static local image imports and static JSX string-literal asset references, and writes optimized generated copies under .squeezit/babel-assets. It does not mutate source assets and does not expose or use max mode.

The fixture-value helper and JS/TS API report filePath and outputPath relative to the effective cwd, not as absolute machine-specific paths.

Documentation

Usage

squeezit [patterns...] [options]

Pattern Resolution

  • Pass explicit file paths like hero.png
  • Pass shell-style parameters like *.png
  • Pass glob expressions like images/**/*.webp
  • Pass directories like assets
  • If no file parameter is provided, squeezit scans supported image files in the current directory
  • Scanning is non-recursive by default; use -r, --recursive to traverse subdirectories
  • Discovery includes APNG (.apng), JPEG XL (.jxl), ICO (.ico), and CUR (.cur) files

Options

| Option | Description | Default | | ------------------------- | ------------------------------------------------------------------------------------- | ------------------------------ | | -r, --recursive | Recurse into directories when scanning inputs | false | | -m, --max | Use the heaviest lossless compression passes, strip metadata, and force threshold 0 | false | | -s, --strip-meta | Remove EXIF, IPTC, and XMP metadata during compression | false | | --exif | Only strip EXIF/IPTC/XMP metadata without recompressing | false | | -d, --dry-run | Show what would change without writing files | false | | -k, --keep-time | Preserve original access and modification timestamps | false | | -c, --concurrency <n> | Set worker concurrency manually | CPU count, or 2 with --max | | -I, --install-deps | Attempt to install missing system tools | false | | -U, --update | Update squeezit to the latest published version | false | | --check-update | Check whether a newer published version exists | false | | --pm <manager> | Override the package manager used for self-update | auto-detected when possible | | -v, --verbose | Print additional diagnostic details | false | | -t, --threshold <bytes> | Minimum savings required before replacing a file | 100 | | -i, --in-place | Create temporary work artifacts next to the source files | false | | -V, --version | Print the current version | n/a | | -h, --help | Show CLI help | n/a |

Examples

Preview everything under the current directory:

squeezit -d

Compress a single file:

squeezit ./images/cover.png

Compress every JPEG under assets, but only if the win is at least 1 KB:

squeezit -r "assets/**/*.jpg" -t 1024

Use the heaviest compression strategy:

squeezit -r "images/**/*" -m

Strip metadata only:

squeezit --exif "photos/**/*.{jpg,tiff,heic}"

Preserve timestamps while stripping metadata:

squeezit -r "photos/**/*.{jpg,tiff,heic}" -s -k

Dry-run a JPEG XL file:

squeezit artwork.jxl -d

Modernize an ICO while preserving its icon sizes:

squeezit app.ico

Modernize a cursor container while preserving entry sizes and hotspots:

squeezit pointer.cur

Update the global installation explicitly with npm:

squeezit -U --pm npm

Supported Inputs

Squeezit currently matches these file extensions during discovery:

  • jpg, jpeg
  • png, apng
  • gif
  • webp
  • svg
  • tif, tiff
  • heic, heif
  • avif
  • bmp
  • jxl
  • ico, cur
  • cr2, nef, arw, raf, orf, rw2

Internally, compression behavior is determined with MIME detection where applicable, not only by extension.

Supported Formats

Squeezit currently supports these image format families:

  • JPEG (.jpg, .jpeg): fast lossless optimization by default, heavier passes in --max
  • PNG (.png): fast oxipng optimization by default, heavier candidate comparison in --max
  • APNG (.apng, animated PNG payloads): optimized losslessly with oxipng
  • GIF (.gif): fast lossless optimization by default, strongest gifsicle pass in --max
  • WebP (.webp): lossless re-encode, with heavier encoder settings in --max, including animated WebP handling
  • SVG (.svg): single-pass optimization by default, multipass in --max
  • TIFF (.tif, .tiff): lossless ZIP recompression, with a heavier ZIP preset in --max
  • HEIF / HEIC (.heif, .heic): lossless re-encode, with a slower encoder preset in --max
  • AVIF (.avif): lossless re-encode, with a slower encoder speed in --max
  • BMP (.bmp): lossless RLE recompression for source 4-bit and 8-bit BMPs only; higher-bit BMPs are skipped
  • JPEG XL (.jxl): lossless re-encode, with a faster default pass and multi-effort candidate comparison in --max
  • ICO (.ico): modernized by extracting embedded icon images, optimizing them, and rebuilding the icon container while preserving the original entry dimensions; if the rebuilt icon changes the dimension set, it is skipped
  • CUR (.cur): modernized by extracting embedded cursor images, optimizing them, and rebuilding the cursor container while preserving the original entry dimensions and hotspot coordinates; if the rebuilt cursor changes either, it is skipped
  • RAW camera files (.cr2, .nef, .arw, .raf, .orf, .rw2): metadata stripping in --exif mode, optional RAW-to-DNG conversion in --max mode using the smallest lossless DNG settings

Notes:

  • If a lossless result is larger, the file is skipped and never replaced
  • --exif is metadata-only mode and does not run recompression pipelines
  • --max always strips metadata in addition to raising encoder effort across the supported recompression pipelines
  • --max forces the replacement threshold to 0, so any positive lossless reduction is accepted
  • ICO support is focused on modernizing containers while preserving icon sizes, not preserving original legacy BMP-style encoding byte-for-byte
  • CUR support is focused on modernizing containers while preserving entry sizes and cursor hotspots, not preserving original legacy BMP-style encoding byte-for-byte
  • BMP metadata-only writing is not supported; BMP optimization only rewrites eligible indexed BMP image data
  • ICO and CUR metadata-only writing are not supported
  • RAW files are special-case inputs and only convert to .dng in --max mode
  • RAW --max conversion now targets the smallest lossless DNG by disabling embedded RAW, preview, and thumbnail payloads; .rw2 inputs also try the available lossless JPEG predictor variants and keep the smallest result

System Dependencies

Squeezit orchestrates native image tools based on the inputs you actually process. It may require binaries such as:

  • file
  • jpegtran, jpegrescan, jpegoptim
  • pngcrush, optipng, zopflipng, oxipng
  • gifsicle
  • svgo
  • cwebp, dwebp, webpinfo, gif2webp
  • heif-enc
  • avifenc
  • tiffcp
  • magick
  • exiftool
  • cjxl
  • icotool
  • dnglab for RAW to DNG conversion in --max mode

Not every run needs every tool. Dependency checks are format-aware, so squeezit only asks for the binaries needed for the files you matched.

Self-Update

Squeezit can check for a new published version and update itself:

squeezit --check-update
squeezit -U

Installer detection works like this:

  • On install, squeezit records whether it was installed by npm or bun in its config metadata
  • On update, it reuses that persisted installer when available
  • If detection is ambiguous, pass --pm npm or --pm bun

Examples:

squeezit -U --pm npm
squeezit -U --pm bun

If dependencies are missing, you can ask squeezit to install them:

squeezit --install-deps

Supported installation targets:

  • macOS via Homebrew
  • Debian/Ubuntu via APT

Development

This project publishes a Node-targeted CLI, but uses Bun for local development.

Install dependencies:

bun install

Run the source CLI:

bun run index.ts --help

Build the published artifact:

bun run build

Run the compiled CLI locally:

node ./dist/index.js --help

Validate the project:

bun run typecheck
bun test

License

MIT