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

@imageforge/cli

v0.1.10

Published

Build-time image pipeline for web apps: responsive WebP/AVIF derivatives, blur placeholders, manifests, and CI freshness checks.

Readme

Generate optimized derivatives (webp, avif) and blurDataURL placeholders with hash-based caching.

Features

  • One command for image conversion + manifest generation
  • Blur placeholder generation for next/image (blurDataURL)
  • Hash-based cache for fast reruns
  • Bounded parallel processing with --concurrency
  • Deterministic CI guard with --check
  • Structured machine output with --json

Install

Runtime requirement: Node.js >= 20.

Install globally:

npm install -g @imageforge/cli

Run without global install:

npx @imageforge/cli ./public/images --dry-run

Quick Start

imageforge ./public/images --dry-run

Review the preview, then apply the same command without --dry-run:

imageforge ./public/images

By default this writes:

  • Derivatives next to source files (for example hero.jpg -> hero.webp)
  • Cache file at ./public/images/.imageforge-cache.json
  • Manifest at ./imageforge.json

Generate both formats:

imageforge ./public/images --formats webp,avif

Write outputs to a dedicated directory:

imageforge ./public/images --out-dir ./public/generated

Generate responsive width variants:

imageforge ./public/images --formats webp,avif --widths 320,640,960,1280

--widths values are requested targets. ImageForge generates effective widths that do not exceed the source image dimensions (no upscaling).

CLI Usage

imageforge <directory> [options]

| Option | Description | | -------------------------------------------- | ----------------------------------------------------------------------------------- | | -o, --output <path> | Manifest output path (default: imageforge.json) | | -f, --formats <formats> | Output formats, comma-separated (default: webp) | | -q, --quality <number> | Output quality 1..100 (default: 80) | | --blur / --no-blur | Enable/disable blur placeholder generation | | --blur-size <number> | Blur dimensions 1..256 (default: 4) | | --widths <list> | Requested width targets as comma-separated integers (source-bounded, max 16 unique) | | --cache / --no-cache | Enable/disable cache reads/writes | | --force-overwrite / --no-force-overwrite | Allow/disallow overwriting existing outputs | | --check / --no-check | Check outputs + cache + manifest for CI (exit 1 when stale) | | --dry-run / --no-dry-run | Preview processing without writing outputs, manifest, or cache | | --include <pattern> | Include input-relative glob pattern (repeatable or comma-separated) | | --exclude <pattern> | Exclude input-relative glob pattern (repeatable or comma-separated) | | --out-dir <path> | Output directory for generated derivatives | | --concurrency <number> | Parallel processing (1..64, default: min(8, availableParallelism)) | | --json / --no-json | Emit machine-readable JSON report to stdout | | --verbose / --no-verbose | Show additional diagnostics | | --quiet / --no-quiet | Suppress per-file non-error logs | | --config <path> | Explicit JSON config path | | -V, --version | Print version | | -h, --help | Print help |

Runtime Behavior

  • Normal runs exit with code 1 if any file fails processing.
  • --check exits 1 when a source needs processing or the cache/manifest is missing, invalid, or stale; otherwise it exits 0.
  • Symlinks are skipped during discovery.
  • Output collision checks are case-insensitive.
  • Existing outputs are protected unless explicitly overwritten with --force-overwrite.
  • With --check, ImageForge prints a generation command matching the effective options. The CLI detects npm, pnpm, Yarn, or Bun invocations. It uses the exact project-installed version when present; otherwise the hint names the exact scoped package and version, never the unrelated unscoped imageforge package. Treat rerunCommand as a shell-dependent hint and keep the canonical invocation in a package script. If cache provenance is missing or malformed, inspect existing derivatives before removing conflicts or adding --force-overwrite intentionally.
  • --dry-run previews which images would be processed but performs no output, manifest, cache, directory, or lock writes.
  • --check and --dry-run cannot be used together.
  • Responsive width sets are opt-in via --widths (default behavior is unchanged).
  • Requested widths are targets; generated effective widths may be smaller for source-bounded runs.
  • Width lists are capped at 16 unique values to bound compute and output fan-out.
  • Full behavior contract: docs/product/responsive-widths-contract.md.

Responsive Guardrail

ImageForge enforces a maximum of 16 unique requested widths per run/config. This guard keeps responsive generation predictable and reduces accidental or hostile CPU/IO amplification from oversized width lists.

Configuration

Scaffold a starter config:

imageforge init

Overwrite an existing scaffold:

imageforge init --force

Config resolution order:

  1. Internal defaults
  2. Config file (--config <path>, otherwise imageforge.config.json, otherwise package.json#imageforge)
  3. CLI flags

Unknown config keys fail fast.

Example imageforge.config.json:

{
  "output": "imageforge.json",
  "formats": ["webp", "avif"],
  "quality": 80,
  "blur": true,
  "blurSize": 4,
  "widths": [320, 640, 960, 1280],
  "cache": true,
  "dryRun": false,
  "include": ["**/*.jpg", "**/*.png"],
  "exclude": ["**/legacy/**"],
  "outDir": "public/generated",
  "concurrency": 4
}

JSON Output

Use --json to emit a structured report:

imageforge ./public/images --json

The report includes:

  • Effective options
  • Per-image status (processed, cached, failed, needs-processing)
  • Effective generated widths in images[*].variants[*].width when --widths is used
  • Summary counters and size totals
  • Non-fatal warnings, including cache-owned derivatives made obsolete by a formats, widths, or output-contract change
  • Effective-option generation hint for --check failures, with protected recovery when cache provenance is unavailable

Manifest

Manifest shape (imageforge.json):

{
  "version": "1.0",
  "generated": "2026-02-08T00:00:00.000Z",
  "images": {
    "hero.jpg": {
      "width": 1920,
      "height": 1280,
      "aspectRatio": 1.5,
      "blurDataURL": "data:image/png;base64,...",
      "originalSize": 345678,
      "outputs": {
        "webp": { "path": "hero.w1280.webp", "size": 50210 },
        "avif": { "path": "hero.w1280.avif", "size": 31100 }
      },
      "variants": {
        "webp": [
          { "width": 320, "height": 213, "path": "hero.w320.webp", "size": 9012 },
          { "width": 640, "height": 427, "path": "hero.w640.webp", "size": 17654 },
          { "width": 960, "height": 640, "path": "hero.w960.webp", "size": 33210 },
          { "width": 1280, "height": 853, "path": "hero.w1280.webp", "size": 50210 }
        ],
        "avif": [
          { "width": 320, "height": 213, "path": "hero.w320.avif", "size": 6010 },
          { "width": 640, "height": 427, "path": "hero.w640.avif", "size": 12203 },
          { "width": 960, "height": 640, "path": "hero.w960.avif", "size": 21998 },
          { "width": 1280, "height": 853, "path": "hero.w1280.avif", "size": 31100 }
        ]
      },
      "hash": "abc123..."
    }
  }
}

Notes:

  • Manifest keys and output paths are input-directory-relative POSIX paths.
  • When using --out-dir, output paths remain relative to the input directory.
  • If --out-dir is outside the input tree, manifest paths may include ../ segments.
  • When --widths is used, outputs.<format> points to the largest generated variant.
  • variants[*].width stores effective generated widths (requested values filtered by source size).

Next.js Integration Example

import manifest from "./imageforge.json";
import type { ImageForgeEntry } from "@imageforge/cli";

const images = manifest.images as Record<string, ImageForgeEntry>;

export function getImageData(src: string): ImageForgeEntry {
  const image = images[src];
  if (!image) throw new Error(`ImageForge manifest entry not found: ${src}`);
  return image;
}

function joinPublicPath(base: string, outputPath: string): string {
  return `${base.replace(/\/?$/, "/")}${outputPath}`;
}

export function getImageUrl(src: string, format: "webp" | "avif", publicBase = "/images/"): string {
  const output = getImageData(src).outputs[format];
  if (!output) throw new Error(`ImageForge ${format} output not found: ${src}`);
  return joinPublicPath(publicBase, output.path);
}

Then serve a generated derivative. When ./public/images is the input directory, manifest output paths are relative to /images/:

import Image from "next/image";

const hero = getImageData("hero.jpg");

<Image
  src={getImageUrl("hero.jpg", "webp")}
  width={hero.width}
  height={hero.height}
  alt="Product screenshot"
  placeholder="blur"
  blurDataURL={hero.blurDataURL}
  unoptimized
/>;

unoptimized is intentional: it serves ImageForge's pre-generated file instead of sending it through the Next.js runtime image optimizer again. For art direction or multiple formats, render a native <picture> using manifest variants and an accurate sizes attribute.

Optional srcset helper for responsive variants:

export function getSrcSet(src: string, format: "webp" | "avif", publicBase = "/images/") {
  const variants = getImageData(src).variants?.[format];
  return variants
    ?.map((variant) => `${joinPublicPath(publicBase, variant.path)} ${variant.width}w`)
    .join(", ");
}

Programmatic API

ImageForge supports both ESM (import) and CJS (require) consumers.

Root exports processor helpers and manifest types.

Runner functions are exposed on a stable subpath API: @imageforge/cli/runner.

ESM:

import * as imageforge from "@imageforge/cli";
import * as processor from "@imageforge/cli/processor";
import { getDefaultConcurrency, runImageforge } from "@imageforge/cli/runner";

CJS:

const imageforge = require("@imageforge/cli");
const processor = require("@imageforge/cli/processor");
const { getDefaultConcurrency, runImageforge } = require("@imageforge/cli/runner");

Useful root exports include processImage, convertImage, generateBlurDataURL, and manifest types. The runner API is intentionally subpath-only and semver-stable.

Source Input Scope

Current supported source extensions:

  • jpg, jpeg, png, gif, tiff, tif

Notes:

  • webp and avif source files are currently excluded as inputs.
  • GIF handling is static-only (first frame).
  • Source-input expansion roadmap: docs/product/source-input-roadmap.md.

CI Mode

Install ImageForge as a pinned project dependency so CI uses the version recorded in your lockfile:

pnpm add --save-dev --save-exact @imageforge/cli

Add repeatable scripts:

{
  "scripts": {
    "images:build": "imageforge ./public/images --formats webp,avif --widths 320,640,960,1280",
    "images:check": "imageforge ./public/images --formats webp,avif --widths 320,640,960,1280 --check"
  }
}

Commit the manifest, cache, and generated derivatives, then verify them after a frozen install:

pnpm install --frozen-lockfile
pnpm run images:check

--check is read-only and fails if an input needs processing or the checked-in cache/manifest is missing, invalid, or stale. Its failure output includes a build command with the effective options. Review shell quoting before running it, especially for include/exclude patterns containing spaces or shell metacharacters. Cache v2 verifies derivative/blur-metadata SHA-256 digests and generator identity; run one unfiltered regeneration after a v1/legacy cache or ImageForge, Sharp, or libvips change so every entry gets current integrity metadata.

See the focused guides for CI and generated assets, manifest semantics, and Next.js delivery, plus installation and generated-state troubleshooting. These absolute links remain useful from the npm package page. The focused guides are intentionally not bundled in the package tarball.

Benchmarking

CI-native benchmark tooling and contracts live in docs/benchmark/.

  • Standard and thresholds: docs/benchmark/STANDARD.md
  • Data contracts: docs/benchmark/INTERFACES.md
  • Operational runbook: docs/benchmark/RUNBOOK.md
  • Dataset policy: docs/benchmark/DATASET_POLICY.md

Core commands:

pnpm run bench:dataset:download -- --dataset-version 1.0.0 --tier tier30 --out-dir /tmp/imageforge-bench-dataset
pnpm run bench:run -- --cli-path ./dist/cli.js --tier-manifest /tmp/imageforge-bench-dataset/extracted/tier30/tier-manifest.json --workspace /tmp/imageforge-bench-run --run-count 4 --profiles P1,P2,P3
pnpm run bench:compare -- --base-summary /tmp/base-summary.json --head-summary /tmp/head-summary.json --out-json /tmp/compare.json --out-md /tmp/compare.md
pnpm run bench:report -- --head-summary /tmp/head-summary.json --base-summary /tmp/base-summary.json --compare /tmp/compare.json --out /tmp/report.md

Development

pnpm install
pnpm build
pnpm run typecheck
pnpm run lint
pnpm run format:check
pnpm test
pnpm run test:mutation:pilot
pnpm run check

Quality checks run in CI on Node 20, 22, and 24. Mutation testing runs as an advisory pilot in CI (non-blocking), uploads mutation artifacts, and reports score trend deltas against .github/mutation-baseline.json.

Release Workflow

  • Semantic PR titles are enforced in CI; commit-message lint is currently informational unless branch-protection policy is changed.
  • Releases and CHANGELOG.md updates are automated via Release Please.
  • Tags follow annotated SemVer with v prefix (for example v0.1.3).
  • npm publish workflow uses GitHub OIDC trusted publishing.

Run the local pre-release gate before publishing:

pnpm run release:verify

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md.

Code of Conduct

See CODE_OF_CONDUCT.md.

License

MIT