Batch convert images to WebP/AVIF — fully local, no server, no API calls.
Docs and web app: leano.dev
npx leano-cli ./public --format webp --quality 80 --max-width 1920
Recursively scans a directory, converts every .jpg, .jpeg, .png to WebP
and/or AVIF using sharp, preserves the full
folder structure, and reports per-file savings.
npm install -g leano-cli
# or run without installing:
npx leano-cli <input> [options]leano-cli <input> [options]
Arguments:
input Directory of images to convert
Options:
-f, --format <format> Output format: webp | avif | both (default: webp)
-q, --quality <number> Compression quality 1–100 (default: 80)
--lossless Lossless compression
--max-width <px> Maximum output width (no upscaling)
--max-height <px> Maximum output height (no upscaling)
-o, --out <path> Output directory (default: <input>-optimized)
--in-place Replace source directory safely via temp dir
-c, --concurrency <n> Parallel jobs (default scales with CPU; cap webp 16 / both 12 / avif 8)
--quiet No per-file rows (spinner + summary only)
--dry-run List planned outputs only
-h, --help Show help
-V, --version Show version
# WebP at quality 80 (default)
leano-cli ./images
# Both WebP + AVIF
leano-cli ./images --format both --quality 75
# Resize + convert
leano-cli ./public/photos --format webp --max-width 1920 --quality 85
# Lossless WebP
leano-cli ./assets --format webp --lossless
# Custom output directory
leano-cli ./src/images --format avif --out ./dist/images
# Overwrite source in-place (safe: uses temp dir, rolls back on failure)
leano-cli ./public --format webp --in-placeleano-cli v1.0.2
Input: /home/user/project/public/images
Output: /home/user/project/public/images-optimized
Format: both Quality: 80
Files: 42 images found → 84 outputs
File Original Converted Savings
─────────────────────────────────────────────────────────────────────────────
hero.webp 1.2 MB 149 KB 88%
hero.avif 1.2 MB 218 KB 82%
icons/logo.webp 45 KB 12 KB 73%
icons/logo.avif 45 KB 9 KB 80%
...
─────────────────────────────────────────────────────────────────────────────
✔ 84 files converted
✔ 56.2 MB → 9.1 MB (84% saved)
Output: /home/user/project/public/images-optimized
| Extension | Action |
|---|---|
.jpg / .jpeg |
Re-encoded via sharp |
.png |
Re-encoded via sharp |
.webp / .avif |
Copied as-is (no re-encoding) |
- All files are written to a temporary directory first.
- Only if every conversion succeeds, the source directory is replaced atomically.
- On any failure the source is left completely untouched and the temp dir is cleaned up.
| Code | Meaning |
|---|---|
0 |
All files processed OK |
1 |
One or more files failed |
src/
index.ts Entry point & orchestration
cli.ts Argument parsing (commander)
scan.ts Recursive directory traversal
convert.ts worker-thread pool (one sharp pipeline per core slot) + job scheduling
workers/ encodeWorker.ts + encodePool.ts — parallel Node workers for CPU-bound encode
writer.ts Output & in-place replacement logic
logger.ts Table, summary, formatting helpers
types.ts Shared TypeScript types
- Node.js ≥ 18
- Runs on Linux, macOS, Windows (sharp ships pre-built binaries)