squeezit
v1.6.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.
Maintainers
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 squeezitbun
bun add -g squeezitAfter installation, both commands are available:
squeezit --help
sqz --helpQuick Start
Compress supported images in the current directory:
squeezitPreview changes without modifying files:
squeezit -dStrip metadata only, without recompressing:
squeezit --exifTarget 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 -dRun the shorter alias:
sqz -r assets/**/*.jpg -dCheck for a newer published version:
squeezit --check-updateSelf-update to the latest release:
squeezit -UIntegrations
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" - Vite plugin via
import { squeezitVite } from "squeezit/vite"
Planned package subpaths:
squeezit/gulpsqueezit/gruntsqueezit/webpacksqueezit/rollupsqueezit/nextsqueezit/esbuild
The subpath exports are reserved now so future wrappers can ship without forcing a package split. For now, the supported programmatic integration surfaces are the root JS/TS API and the Vite plugin.
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.
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,
squeezitscans supported image files in the current directory - Scanning is non-recursive by default; use
-r, --recursiveto traverse subdirectories - Discovery includes APNG (
.apng), JPEG XL (.jxl), and ICO (.ico) 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 -dCompress a single file:
squeezit ./images/cover.pngCompress every JPEG under assets, but only if the win is at least 1 KB:
squeezit -r "assets/**/*.jpg" -t 1024Use the heaviest compression strategy:
squeezit -r "images/**/*" -mStrip metadata only:
squeezit --exif "photos/**/*.{jpg,tiff,heic}"Preserve timestamps while stripping metadata:
squeezit -r "photos/**/*.{jpg,tiff,heic}" -s -kDry-run a JPEG XL file:
squeezit artwork.jxl -dModernize an ICO while preserving its icon sizes:
squeezit app.icoUpdate the global installation explicitly with npm:
squeezit -U --pm npmSupported Inputs
Squeezit currently matches these file extensions during discovery:
jpg,jpegpng,apnggifwebpsvgtif,tiffheic,heifavifbmpjxlicocr2,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--maxPNG(.png): fastoxipngoptimization by default, heavier candidate comparison in--maxAPNG(.apng, animated PNG payloads): optimized losslessly withoxipngGIF(.gif): fast lossless optimization by default, strongestgifsiclepass in--maxWebP(.webp): lossless re-encode, with heavier encoder settings in--max, including animated WebP handlingSVG(.svg): single-pass optimization by default, multipass in--maxTIFF(.tif,.tiff): lossless ZIP recompression, with a heavier ZIP preset in--maxHEIF / HEIC(.heif,.heic): lossless re-encode, with a slower encoder preset in--maxAVIF(.avif): lossless re-encode, with a slower encoder speed in--maxBMP(.bmp): lossless RLE recompression for source 4-bit and 8-bit BMPs only; higher-bit BMPs are skippedJPEG XL(.jxl): lossless re-encode, with a faster default pass and multi-effort candidate comparison in--maxICO(.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 skippedRAW camera files(.cr2,.nef,.arw,.raf,.orf,.rw2): metadata stripping in--exifmode, optional RAW-to-DNG conversion in--maxmode using the smallest lossless DNG settings
Notes:
- If a lossless result is larger, the file is skipped and never replaced
--exifis metadata-only mode and does not run recompression pipelines--maxalways strips metadata in addition to raising encoder effort across the supported recompression pipelines--maxforces the replacement threshold to0, 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
- BMP metadata-only writing is not supported; BMP optimization only rewrites eligible indexed BMP image data
- RAW files are special-case inputs and only convert to
.dngin--maxmode - RAW
--maxconversion now targets the smallest lossless DNG by disabling embedded RAW, preview, and thumbnail payloads;.rw2inputs 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:
filejpegtran,jpegrescan,jpegoptimpngcrush,optipng,zopflipng,oxipnggifsiclesvgocwebp,dwebp,webpinfo,gif2webpheif-encavifenctiffcpmagickexiftoolcjxlicotooldnglabfor RAW to DNG conversion in--maxmode
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 -UInstaller detection works like this:
- On install,
squeezitrecords whether it was installed bynpmorbunin its config metadata - On update, it reuses that persisted installer when available
- If detection is ambiguous, pass
--pm npmor--pm bun
Examples:
squeezit -U --pm npm
squeezit -U --pm bunIf dependencies are missing, you can ask squeezit to install them:
squeezit --install-depsSupported 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 installRun the source CLI:
bun run index.ts --helpBuild the published artifact:
bun run buildRun the compiled CLI locally:
node ./dist/index.js --helpValidate the project:
bun run typecheck
bun test