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

vite-image-pipeline

v1.0.4

Published

Framework-agnostic image processing pipeline building blocks for Vite

Readme

vite-image-pipeline

A high-performance, asset-caching image pipeline designed for Vite-based projects (Astro, React, Svelte, Vue, Solid, and more). It extracts rich metadata, generates local ML-driven vector embeddings, computes low-resolution blur placeholders, samples dominant colors, and handles zero-egress remote platform uploads during production builds.

Root entry point works without Astro. Import Astro lifecycle support separately:

import { getImageColors } from "vite-image-pipeline";
import { astroImagePipelinePlugin } from "vite-image-pipeline/astro";

While vite-image-pipeline is completely framework-agnostic and can be utilized in any Vite context, it also includes a dedicated integration for Astro to automatically coordinate cleanup and remote synchronization on production builds.

Why Use This?

This package is designed to work seamlessly in conjunction with image compression and optimization tools such as vite-imagetools or Astro's native Image Component.

Instead of replacing image optimizers, it splits the pipeline into two parallel, specialized paths that merge gracefully at the UI layer:

                      [ Source Image Asset ]
                                │
            ┌───────────────────┴───────────────────┐
            ▼                                       ▼
┌───────────────────────┐               ┌───────────────────────┐
│  vite-image-pipeline  │               │   vite-imagetools /   │
│  (Data Extraction)    │               │    Astro's <Image>    │
├───────────────────────┤               ├───────────────────────┤
│ • EXIF Metadata       │               │ • Multi-format output │
│ • Blur Placeholders   │               │   (WebP, AVIF, JPEG)  │
│ • Dominant Colors     │               │ • Fluid UI Resizing   │
│ • Semantic Vectors    │               │ • File Compression    │
└───────────┬───────────┘               └───────────┬───────────┘
            │                                       │
            ▼                                       ▼
 [ JSON Cache / Cloud CDN ]             [ Optimized Web Assets ]
            │                                       │
            └───────────────────┬───────────────────┘
                                ▼
                    ┌───────────────────────┐
                    │      Frontend UI      │
                    │ (Astro / React / etc) │
                    ├───────────────────────┤
                    │ • Low-CLS Blur loading│
                    │ • Dynamic BG Tints    │
                    │ • Semantic Search     │
                    └───────────────────────┘

It processes your original source assets to power rich frontend features like semantic visual search, dynamic UI color-matching, and custom lazy-loading states, leaving final layout rendering, sizing, and responsive resizing to your preferred UI layer.

Features

  • Flexible Path Resolution: Fully supports arbitrary filesystem structures. Paths passed to data extraction functions can be project-relative (e.g., /src/assets/photo.jpg), project-root relative (e.g., src/assets/photo.jpg), or absolute global paths (e.g., /Users/username/Desktop/photo.jpg).
  • Aggressive Content-Based Caching: Cross-references file modification times (mtime), sizes, and fast initial-block cryptographic hashes to guarantee assets are processed exactly once unless edited.
  • EXIF Metadata Extraction: Powered by exiftool-vendored for lightning-fast, comprehensive tag reading (Camera model, lens, exposure, timestamps, geo-coordinates).
  • Local Machine Learning Embeddings: Generates normalized semantic image vectors locally using ONNX runtime and Transformers.js (Xenova/clip-vit-base-patch32). Perfect for image-to-image or text-to-image similarity matching.
  • UI Enhancements: Generates ultra-fast low-res base64 blur placeholders and dominant color palettes using native sharp bindings.
  • Cloud Upload Sync & Zero-Egress Caching: Offloads assets to remote S3-compatible endpoints (like Cloudflare R2) sequentially during production builds. Leverages IfNoneMatch ETag validation to skip uploading unmodified files and purges local build items post-upload to keep your bundle footprint tiny.
  • Race-Condition Safe: Implements an internal promise-locking sequence to guarantee thread/hook-safe evaluation inside heavily parallelized Vite or Astro multi-threaded builds.

Installation

Install the package via your preferred package manager:

npm install vite-image-pipeline

Path Resolution Rules

The image pipeline intelligently handles the formatting differences introduced by bundler hooks and components across frameworks:

  1. Project-Relative Paths (/src/*): Resolves paths starting with /src/ straight against your current working directory (process.cwd()).
  2. Absolute Global Paths: Fully supports native system roots (/Users/, /home/, or Windows drive letters) for scenarios where images are processed out of external directories.
  3. Vite Internals (/@fs/*): Automatically cleans up internal prefixes injected by Vite's dev server to match actual file boundaries cleanly.
import { getMetadata } from 'vite-image-pipeline';

// All map keys are fully compatible and safely mapped:
const data = await getMetadata([
  '/src/assets/gallery/photo1.jpg',                // Project relative
  'src/assets/gallery/photo2.jpg',                 // Root relative
  '/Users/alex/Projects/site/src/assets/photo3.jpg' // Absolute global path
]);

Advanced Configuration

You can customize cache paths, concurrency profiles, and machine learning runtime characteristics globally at the entry point of your pipeline execution using setOptions.

Complete Options Schema

import { setOptions } from 'vite-image-pipeline';

setOptions({
  // The HuggingFace/Transformers.js repository string for feature extraction
  modelName: "Xenova/clip-vit-base-patch32", 
  
  // Number of images processed concurrently during ML vector inference
  batchSize: 4,                              
  
  // Fully qualified filesystem targets for the localized JSON content-caches
  metadataCachePath: "./.image-pipeline/metadata-cache.json",
  embeddingCachePath: "./.image-pipeline/embedding-cache.json",
  colorCachePath: "./.image-pipeline/color-cache.json",
  blurCachePath: "./.image-pipeline/blur-cache.json",
  
  // Target directory where local ONNX weights and tokenizer models are written
  modelCachePath: "./.image-pipeline/models"
});

Call flushImagePipelineCaches() before a short-lived script exits. Astro integration flushes pending writes automatically. Cache writes are atomic, so an interrupted process cannot replace valid JSON with partial JSON.

Full API Reference & Examples

1. Metadata Extraction

Reads embedded EXIF, IPTC, and XMP metadata out of original source files.

import { getMetadata } from 'vite-image-pipeline';

const images = ['src/assets/photo1.jpg', 'src/assets/photo2.jpg'];
const metadataMap = await getMetadata(images);

const photo1 = metadataMap['src/assets/photo1.jpg'];
console.log(`Camera: ${photo1.Model}, Lens: ${photo1.LensModel}, ISO: ${photo1.ISO}`);

2. Machine Learning Embeddings

Generates a normalized 512-dimensional vector embedding array. These arrays can be sent straight into vector stores (like Chroma, Pinecone, or pgvector) for semantic searching.

import { getEmbeddings } from 'vite-image-pipeline';

const embeddingsMap = await getEmbeddings(['src/assets/photo1.jpg']);
const vector = embeddingsMap['src/assets/photo1.jpg']; 
// Output: [0.0124, -0.0452, 0.0911, ... 512 floats long]

3. Low-Resolution Blur Placeholders

Generates a 20px wide blurred base64 JPEG/PNG Data URI to avoid Cumulative Layout Shift (CLS) during image lazy-loading.

import { getImageBlurPlaceholders } from 'vite-image-pipeline';

const blurMap = await getImageBlurPlaceholders(['src/assets/photo1.jpg']);
const placeholderUri = blurMap['src/assets/photo1.jpg'];
// Output: "data:image/jpeg;base64,/9j/4AAQSkZJR..."

4. Dominant Color Sampling

Extracts the principal structural RGB values of an image to match container background colors before asset load execution completes.

import { getImageColors } from 'vite-image-pipeline';

const colorsMap = await getImageColors(['src/assets/photo1.jpg']);
const { r, g, b } = colorsMap['src/assets/photo1.jpg'];
console.log(`Dominant Background: rgb(${r}, ${g}, ${b})`);

5. Remote Cloud Upload Registry

Queues assets to pass onto remote storage during production compiles. Automatically bypasses uploads during development mode (import.meta.env.PROD === false).

import { uploadRemoteImages } from 'vite-image-pipeline';

const remoteOptions = {
  platform: 'cloudflare-r2',
  accountId: process.env.R2_ACCOUNT_ID,
  r2AccessKey: process.env.R2_ACCESS_KEY,
  r2SecretKey: process.env.R2_SECRET_KEY,
  bucketName: 'my-gallery-cdn',
  outDir: 'dist', // Local output distribution path to read from and prune
  // Browser-facing origin. Uploads still use account R2 API endpoint.
  bucketDomain: 'download.example.com',
  cacheControl: 'public, max-age=31536000, immutable'
};

const transformedUrls = await uploadRemoteImages(remoteOptions, ['src/assets/photo1.jpg']);
// Dev output:  ['src/assets/photo1.jpg']
// Prod output: ['https://download.example.com/src/assets/photo1.jpg']

Comprehensive Implementation Recipes

Astro (Production SSG / SSR)

Add the integration manager hook to your astro.config.mjs setup to coordinate graceful engine shutdown (exiftool worker pools) and handle file uploading sequences during astro:build:done.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import { astroImagePipelinePlugin } from 'vite-image-pipeline/astro';

export default defineConfig({
  integrations: [
    astroImagePipelinePlugin()
  ]
});

Inside an Astro Component (src/components/SmartImage.astro)

---
import { Image } from 'astro:assets';
import { getImageBlurPlaceholders, getImageColors, getMetadata } from 'vite-image-pipeline';

interface Props {
  imageAsset: any; // Astro ESM Image Import Reference
  alt: string;
}

const { imageAsset, alt } = Astro.props;

// 1. Resolve structural filesystem pathing safely
const absolutePath = imageAsset.fsPath;

// 2. Fetch data parallelized via content caching locks
const [blurMap, colorMap, metaMap] = await Promise.all([
  getImageBlurPlaceholders([absolutePath]),
  getImageColors([absolutePath]),
  getMetadata([absolutePath])
]);

const blurPlaceholder = blurMap[absolutePath];
const { r, g, b } = colorMap[absolutePath];
const cameraModel = metaMap[absolutePath]?.Model || "Unknown Camera";
---

<div 
  class="image-wrapper" 
  style={`background-color: rgb(${r}, ${g}, ${b}); position: relative; overflow: hidden;`}
>
  <Image 
    src={imageAsset} 
    alt={alt}
    loading="lazy"
    style={`background-image: url(${blurPlaceholder}); background-size: cover;`}
  />
  <span class="exif-overlay">Shot on: {cameraModel}</span>
</div>

<style>
  .image-wrapper img { transition: filter 0.3s ease-out; }
  .exif-overlay { position: absolute; bottom: 8px; left: 8px; color: #fff; font-size: 0.75rem; }
</style>

React + Vite (With vite-imagetools)

In non-Astro build architectures, call the data functions directly within your build runner, build plugins, data-loading environments, or node generation engines.

Step 1: Pre-render Data Config Script (scripts/process-images.js)

Run this process script before or during your main build loop to extract assets details into localized JSON references:

import fs from 'fs/promises';
import glob from 'fast-glob';
import { getImageBlurPlaceholders, getImageColors } from 'vite-image-pipeline';

async function buildStaticManifest() {
  const filePaths = await glob('src/assets/gallery/*.{jpg,jpeg,png,webp,avif}');
  
  const [blurs, colors] = await Promise.all([
    getImageBlurPlaceholders(filePaths),
    getImageColors(filePaths)
  ]);

  const manifest = filePaths.reduce((acc, rawPath) => {
    acc[rawPath] = {
      blur: blurs[rawPath],
      color: colors[rawPath]
    };
    return acc;
  }, {});

  await fs.writeFile('./src/image-manifest.json', JSON.stringify(manifest, null, 2));
  console.log('⚡ Image pipeline data manifest compiled.');
}

buildStaticManifest();

Step 2: Consume Data inside your React UI Engine

import React from 'react';
// 1. Load optimized images via query-params from vite-imagetools
import optimizedHero from '../assets/gallery/lake-sunset.jpg?width=1200&format=avif';
// 2. Import the statically generated metadata mapping file
import imageManifest from '../image-manifest.json';

const ASSET_KEY = 'src/assets/gallery/lake-sunset.jpg';

export function HighPerformanceHero() {
  const uiMeta = imageManifest[ASSET_KEY] || { blur: '', color: { r: 30, g: 30, b: 30 } };
  const { r, g, b } = uiMeta.color;

  return (
    <div 
      className="hero-container" 
      style={{ backgroundColor: `rgb(${r}, ${g}, ${b})`, minHeight: '400px' }}
    >
      <img 
        src={optimizedHero} 
        alt="Lake Sunset" 
        loading="lazy"
        style={{
          backgroundImage: `url(${uiMeta.blur})`,
          backgroundSize: 'cover',
          width: '100%',
          height: 'auto'
        }}
      />
    </div>
  );
}

Troubleshooting

Machine Learning Performance Tweaks

When compiling in standard local environments, Transformers.js splits processes dynamically over multicore nodes. If processing locks up execution inside CI/CD cloud actions (like GitHub Actions runners or Vercel build instances), pin execution to a single worker profile core manually:

NODE_ENV=production npm run build

(The engine sets wasm.numThreads = 1 dynamically when detecting production variables to avoid thread overhead allocation crashes).

Cloudflare R2 Upload Checksums 412 Errors

If you see skipped execution responses reporting PreconditionFailed or 412 HTTP codes during automated deployment pipelines, this behavior is intentional. It guarantees that files whose cryptographic hashes match assets currently hosted on your R2 object storage CDN avoid re-upload workflows, saving you bandwidth and billing resource allocations.