npm package: @srijanpokhrel/image-shrink
Description: Compress AVIF, WebP, JPEG, and PNG images from the CLI or Node.js API — powered by [sharp](https://sharp.pixelplumbing.com/) (libvips).
Latest version: 1.0.3

# image-shrink

Compress AVIF, WebP, JPEG, and PNG images from the CLI or Node.js API — powered by [sharp](https://sharp.pixelplumbing.com/) (libvips).

## Features

- Compress JPEG, PNG, WebP, and AVIF
- Convert between formats (e.g. JPEG → WebP)
- Resize to a max width or height while preserving aspect ratio
- Strip EXIF/GPS metadata
- Batch compress via glob patterns
- Use as a CLI tool or Node.js API

---

## Install

```bash
# CLI (global install)
npm install -g @srijanpokhrel/image-shrink

# Node.js API (project dependency)
npm install @srijanpokhrel/image-shrink
```

> **Note:** Remove the `-g` flag when installing as a project dependency.

---

## CLI Usage

```bash
# Compress a single file (in-place)
image-shrink "photo.jpg"

# Batch compress all JPEGs in a folder
image-shrink "images/**/*.jpg"

# Convert to WebP with custom quality
image-shrink "images/**/*.jpg" --format webp --quality 80

# Resize to max 800px width and output to a folder
image-shrink "images/**/*.jpg" --width 800 --out dist/images

# Convert to AVIF
image-shrink "images/**/*.{jpg,png}" --format avif --quality 60
```

### Options

| Flag | Default | Description |
|------|---------|-------------|
| `--quality`, `-q` | `80` | Compression quality, 1–100 |
| `--format`, `-f` | auto | Output format: `jpeg`, `png`, `webp`, `avif` |
| `--out`, `-o` | in-place | Output directory |
| `--width`, `-w` | — | Max width in px, preserves aspect ratio |
| `--strip` | `true` | Strip EXIF/GPS metadata |

---

## Node.js API

```ts
import { compress } from 'image-shrink';
import fs from 'fs/promises';

const input = await fs.readFile('photo.jpg');

const result = await compress(input, {
  quality: 80,
  format: 'webp',
  stripMetadata: true,
});

await fs.writeFile('photo.webp', result.outputBuffer);

console.log(`Saved ${result.savedPercent}%`);
// → Saved 63%
```

### `compress(input, options)`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `quality` | `number` | `80` | Compression quality, 1–100 |
| `format` | `'jpeg' \| 'png' \| 'webp' \| 'avif'` | auto-detect | Output format |
| `width` | `number` | — | Max width in px, preserves aspect ratio |
| `height` | `number` | — | Max height in px, preserves aspect ratio |
| `stripMetadata` | `boolean` | `true` | Strip EXIF/GPS metadata |
| `lossless` | `boolean` | `false` | Lossless compression (WebP/AVIF only) |
| `effort` | `number` | `4` | Compression effort, 0–9 (AVIF only) |

### `CompressResult`

```ts
{
  inputSize: number;      // original size in bytes
  outputSize: number;     // compressed size in bytes
  savedBytes: number;     // bytes saved
  savedPercent: number;   // % reduction
  outputBuffer: Buffer;   // compressed image data
}
```

---

## Real-World Results

Tested on a high-resolution JPEG (743 KB, 4500×3000 px):

| Command | Output | Saved |
|---------|--------|-------|
| `--quality 75` | 104 KB | **86%** |
| `--format webp --quality 80` | 78 KB | **89%** |
| `--width 800 --quality 75` | 41 KB | **94%** |

> Results vary by image content. High-detail photos compress differently than flat graphics.

---

## Local Development

```bash
git clone https://github.com/crizanp/image-shrink
cd image-shrink
npm install
npm run build
```

### Run Manually

```bash
# Test the CLI directly (no global install needed)
node dist/cli.js "tests/fixtures/sample.jpg" --quality 80

# Watch mode — rebuilds on every save
npm run dev
```

### Run Tests

```bash
npm test

# With coverage report
npm test -- --coverage
```

### Test Results

```
PASS  tests/compress.test.ts
  compress()
    ✓ compresses JPEG and returns smaller output (310ms)
    ✓ converts to WebP (280ms)
    ✓ converts to AVIF (890ms)
    ✓ resizes to max width (200ms)
    ✓ strips EXIF metadata (195ms)
    ✓ savings stats are mathematically correct (190ms)
    ✓ throws on invalid input (12ms)

Tests: 7 passed  |  Time: ~2.5s
```

---

## Scripts

| Command | Description |
|---------|-------------|
| `npm run build` | Compile TypeScript → `dist/` |
| `npm run dev` | Watch mode, rebuild on save |
| `npm test` | Run Jest test suite |
| `npm test -- --coverage` | Tests + coverage report |

---

## License

MIT