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

webpack-plugin-sharp

v1.0.2

Published

webpack / Rspack plugin for image compression using sharp and svgo

Downloads

178

Readme

webpack-plugin-sharp

npm version license npm downloads

中文 | English


中文

基于 sharp(光栅图片)和 svgo(SVG)的 零配置 webpack 图片压缩插件。构建时自动压缩 bundle 产物及输出目录中的所有图片,无需额外设置。

为什么选 webpack-plugin-sharp?

  • 性能:底层使用 libvips(C++ 原生绑定),比 imagemin 快 4–5 倍
  • 全覆盖:同时处理 JS/CSS 引入的 bundle 资源 输出目录中的静态图片
  • 智能缓存:基于文件内容 hash,未修改的文件零开销跳过
  • 安全:压缩后体积更大时保留原图,永不劣化产物
  • 透明:终端逐文件展示压缩前后体积

功能概览

| 能力 | 说明 | |------|------| | 格式支持 | jpeg / jpg / png / gif / tiff / webp / avif(sharp)+ svg(svgo) | | 处理范围 | bundle 内联资源 + 输出目录静态文件 | | 缓存 | 文件内容 hash,可自定义路径或禁用 | | 过滤 | include / exclude 白黑名单 | | 限流 | concurrency 并发控制、minSize / minRatio 阈值 | | 缩放 | resize 限制最大宽高,不放大小图 | | 兼容性 | webpack 4 / 5 · Rspack 2+,仅 build 阶段生效 |

安装

npm install webpack-plugin-sharp -D

快速开始

// webpack.config.js
const WebpackPluginSharp = require("webpack-plugin-sharp");

module.exports = {
  plugins: [
    new WebpackPluginSharp(),
  ],
};

TypeScript 项目:

// webpack.config.ts
import WebpackPluginSharp from "webpack-plugin-sharp";

export default {
  plugins: [
    new WebpackPluginSharp(),
  ],
};

配置项

new WebpackPluginSharp(options?: Partial<OptionsType>)

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | filter | RegExp | /\.(jpe?g\|png\|gif\|tiff\|webp\|svg\|avif)$/i | 匹配需要处理的文件路径 | | include | RegExp \| string \| string[] | — | 白名单(优先于 filter + exclude) | | exclude | RegExp \| string \| string[] | — | 黑名单 | | cache | string \| false | node_modules/.cache/webpack-plugin-sharp/sharp-cache.json | 缓存路径,false 禁用 | | minRatio | number | 0 | 最小压缩率(0–1),低于此值保留原图 | | minSize | number | 0 | 最小文件大小(字节),小于此值跳过 | | concurrency | number | os.cpus().length | 最大并发压缩数 | | resize | ResizeConfig | — | 压缩前缩放(withoutEnlargement: true) | | compress | CompressType | 见下方 | 各格式压缩参数 |

| 格式 | 默认配置 | |------|----------| | jpeg / jpg | quality: 75chromaSubsampling: "4:4:4" | | png | quality: 75compressionLevel: 6palette: true | | webp | quality: 75effort: 4 | | avif | lossless: true | | tiff | quality: 75 | | gif | sharp 默认参数 | | svg | svgo preset-defaultmultipass: true |

完整参数参考:sharp output API · svgo 配置

使用示例

new WebpackPluginSharp({
  compress: {
    jpeg: { quality: 85 },
    png: { quality: 80, compressionLevel: 9 },
    webp: { quality: 80, lossless: false },
  },
});
// 只处理指定目录
new WebpackPluginSharp({ include: /^assets\/images\// });

// 排除特定文件
new WebpackPluginSharp({ exclude: ["logo.svg", "favicon.ico"] });
// 禁用缓存
new WebpackPluginSharp({ cache: false });

// 自定义路径(CI 中可提交复用)
new WebpackPluginSharp({ cache: ".cache/sharp.json" });
new WebpackPluginSharp({
  minRatio: 0.05,       // 节省 < 5% 则保留原图
  minSize: 10240,       // 跳过小于 10 KB 的文件
  resize: { width: 1920, height: 1080 },
  concurrency: 4,
});

终端输出

构建完成后,插件在 webpack 构建摘要之后输出每个文件的压缩结果(体积单位与 webpack 一致,均为 KiB):

webpack 5.x compiled successfully in 1084 ms

[webpack-plugin-sharp] compressing images...
├── dist/assets/hero.02598f1c..png   12.8 KiB → 11.5 KiB │ -1.25 KiB  (9.8%)
├── dist/assets/react.f4beffb2..svg  4.03 KiB → 3.65 KiB │   -390 B   (9.5%)
├── dist/favicon.svg                 1.69 KiB → 1.43 KiB │   -273 B  (15.8%)
├── dist/icon.png                     121 KiB → 97.1 KiB │ -23.9 KiB (19.7%)
└── dist/banner.png                   778 KiB →  239 KiB │  -539 KiB (69.3%) [cached]

✓ 5 files compressed  -565 KiB (61.0%)

| 标识 | 含义 | |------|------| | (无标识) | 本次新鲜压缩 | | [cached] | 缓存命中,跳过计算,直接重用上次压缩结果 | | [skipped] | 压缩收益不足(低于 minRatio / minSize 阈值,或压缩后反而更大),保留原图 |

文件被 exclude / include 过滤后不参与压缩,也不会出现在日志中。

缓存机制

| 场景 | 行为 | |------|------| | 首次构建 | 压缩所有匹配图片,写入 stats 缓存和内容缓存 | | 图片未修改 | 命中 stats 缓存([cached])+ 内容缓存重放压缩字节,输出文件始终为压缩版本 | | 图片修改 | 仅重新压缩该文件,更新两个缓存 | | 文件被 [skipped] | 缓存记录"已跳过"状态,后续构建同样跳过,不会意外应用压缩数据 | | cache: false | 每次都重新压缩,不持久化 |

缓存文件位于 node_modules/.cache/webpack-plugin-sharp/,包含:

  • sharp-cache.json — stats 缓存(文件大小、内容 hash)
  • content/ — 二进制内容缓存(存储压缩后字节),确保连续构建输出始终为压缩版本

CI 场景可指定自定义路径持久化两者。

工作原理

webpack 5 / Rspack Build Pipeline
────────────────────────────────────────────────────────────────
1. compilation.hooks.processAssets (PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE)
   → 处理 bundle 中的图片(内存中压缩)
   → webpack 写入磁盘时即为压缩版本(filename hash 也随之更新)

2. compiler.hooks.afterEmit
   → 扫描输出目录,处理静态复制的图片(如 CopyWebpackPlugin)
   → 磁盘上原地压缩替换
   → 通过内容 hash 去重,避免重复处理 bundle 资源

3. compiler.hooks.done → process.once('beforeExit')
   → 在 webpack 打印构建摘要之后输出压缩统计

webpack 4 (降级路径)
────────────────────────────────────────────────────────────────
1. compiler.hooks.emit(tapAsync)
   → 同等压缩 bundle 资源(compilation.assets 原地替换)

2. compiler.hooks.afterEmit + compiler.hooks.done
   → 同上
────────────────────────────────────────────────────────────────

Rspack 兼容性

Rspack 实现了与 webpack 5 完全兼容的 Plugin API,本插件可直接在 Rspack 项目中使用:

// rspack.config.js
const { rspack } = require("@rspack/core");
const WebpackPluginSharp = require("webpack-plugin-sharp");

module.exports = {
  plugins: [
    new rspack.HtmlRspackPlugin({ template: "./index.html" }),
    // ⚠️ 使用 rspack 内置的 CopyRspackPlugin,而非 copy-webpack-plugin(后者不兼容 Rspack)
    new rspack.CopyRspackPlugin({ patterns: [{ from: "public", to: "." }] }),
    new WebpackPluginSharp(),
  ],
};

插件使用 compiler.webpack 访问 sourcesCompilation,这是 webpack 5 / Rspack 推荐的方式,避免了 CJS / ESM 命名导入的兼容问题。

使用 Vite?

如果你的项目使用 Vite,请改用功能完全等价的姊妹库:

npm install vite-plugin-sharp -D
// vite.config.ts
import VitePluginSharp from "vite-plugin-sharp";

export default {
  plugins: [
    VitePluginSharp(),
  ],
};

两者配置项完全一致(compressresizecacheinclude/exclude 等),可以无缝切换。


开发

npm run build        # 构建插件
npm run dev          # 监听模式
npm test             # 运行测试
npm run test:watch   # 监听测试

许可证

ISC


English

Zero-config webpack image compression plugin powered by sharp (raster) and svgo (SVG). Automatically compresses all images in both the bundle output and the output directory during build.

Why webpack-plugin-sharp?

  • Performance: libvips native bindings — 4–5× faster than imagemin
  • Full coverage: processes JS/CSS bundle assets and static files in the output directory
  • Smart caching: content-hash based, zero overhead for unchanged files
  • Safe: never replaces originals when compression yields a larger file
  • Transparent: per-file terminal output for every processed image

Feature Overview

| Capability | Details | |------------|---------| | Formats | jpeg / jpg / png / gif / tiff / webp / avif (sharp) + svg (svgo) | | Scope | Bundle inline assets + output directory static files | | Caching | Content hash, customizable path or disable | | Filtering | include / exclude allow/deny lists | | Throttling | concurrency limiter, minSize / minRatio thresholds | | Resizing | resize — max dimensions with withoutEnlargement | | Compatibility | webpack 4 / 5 · Rspack 2+, build-only |

Installation

npm install webpack-plugin-sharp -D

Quick Start

// webpack.config.js
const WebpackPluginSharp = require("webpack-plugin-sharp");

module.exports = {
  plugins: [
    new WebpackPluginSharp(),
  ],
};

Options

new WebpackPluginSharp(options?: Partial<OptionsType>)

| Option | Type | Default | Description | |--------|------|---------|-------------| | filter | RegExp | /\.(jpe?g\|png\|gif\|tiff\|webp\|svg\|avif)$/i | Regex matching file paths to process | | include | RegExp \| string \| string[] | — | Allowlist (overrides filter + exclude) | | exclude | RegExp \| string \| string[] | — | Denylist | | cache | string \| false | node_modules/.cache/webpack-plugin-sharp/sharp-cache.json | Cache path, or false to disable | | minRatio | number | 0 | Min savings ratio (0–1) to replace original | | minSize | number | 0 | Min file size in bytes to process | | concurrency | number | os.cpus().length | Max parallel compressions | | resize | ResizeConfig | — | Pre-compress resize (withoutEnlargement: true) | | compress | CompressType | See below | Per-format compression options |

| Format | Defaults | |--------|----------| | jpeg / jpg | quality: 75, chromaSubsampling: "4:4:4" | | png | quality: 75, compressionLevel: 6, palette: true | | webp | quality: 75, effort: 4 | | avif | lossless: true | | tiff | quality: 75 | | gif | sharp defaults | | svg | svgo preset-default, multipass: true |

Full reference: sharp output API · svgo config

Examples

new WebpackPluginSharp({
  compress: {
    jpeg: { quality: 85 },
    png: { quality: 80, compressionLevel: 9 },
    webp: { quality: 80, lossless: false },
  },
});
// Process only a specific directory
new WebpackPluginSharp({ include: /^assets\/images\// });

// Exclude specific files
new WebpackPluginSharp({ exclude: ["logo.svg", "favicon.ico"] });
// Disable caching
new WebpackPluginSharp({ cache: false });

// Custom path (shareable in CI)
new WebpackPluginSharp({ cache: ".cache/sharp.json" });
new WebpackPluginSharp({
  minRatio: 0.05,       // skip if savings < 5%
  minSize: 10240,       // skip files < 10 KB
  resize: { width: 1920, height: 1080 },
  concurrency: 4,
});

Terminal Output

After the build completes, the plugin prints compression results after webpack's build summary (sizes are in KiB, matching webpack's stats output):

webpack 5.x compiled successfully in 1084 ms

[webpack-plugin-sharp] compressing images...
├── dist/assets/hero.02598f1c..png   12.8 KiB → 11.5 KiB │ -1.25 KiB  (9.8%)
├── dist/assets/react.f4beffb2..svg  4.03 KiB → 3.65 KiB │   -390 B   (9.5%)
├── dist/favicon.svg                 1.69 KiB → 1.43 KiB │   -273 B  (15.8%)
├── dist/icon.png                     121 KiB → 97.1 KiB │ -23.9 KiB (19.7%)
└── dist/banner.png                   778 KiB →  239 KiB │  -539 KiB (69.3%) [cached]

✓ 5 files compressed  -565 KiB (61.0%)

| Indicator | Meaning | |-----------|---------| | (none) | Freshly compressed this build | | [cached] | Stats cache hit — compressed bytes replayed from content cache | | [skipped] | Savings below minRatio / minSize threshold, or compressed output was larger — original retained |

Files excluded by exclude / include filters are not processed and will not appear in the log.

Caching

| Scenario | Behavior | |----------|----------| | First build | Compresses all matching images, writes stats + content cache | | File unchanged | Stats cache hit ([cached]) + content cache replays compressed bytes — output is always compressed | | File modified | Only the changed file is recompressed; both caches updated | | File [skipped] | "Skipped" state is persisted in cache — subsequent builds also skip, never accidentally applying stale compressed bytes | | cache: false | Recompresses every file every build, no persistence |

The cache lives in node_modules/.cache/webpack-plugin-sharp/ and contains:

  • sharp-cache.json — stats cache (file sizes, content hash)
  • content/ — binary content cache (compressed bytes), ensuring consecutive builds always produce compressed output

For CI, specify a custom cache path to share both between builds.

How It Works

webpack 5 / Rspack Build Pipeline
────────────────────────────────────────────────────────────────
1. compilation.hooks.processAssets (PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE)
   → Compresses bundle images (JS/CSS imports) in memory
   → webpack writes the compressed version to disk (filename hash updated)

2. compiler.hooks.afterEmit
   → Scans output dir for static images (e.g. from CopyWebpackPlugin)
   → Compresses static files in-place on disk
   → Deduplicates via content hash — no double-processing of bundle assets

3. compiler.hooks.done → process.once('beforeExit')
   → Prints compression stats after webpack's build summary

webpack 4 (fallback path)
────────────────────────────────────────────────────────────────
1. compiler.hooks.emit (tapAsync)
   → Compresses bundle assets in-place via compilation.assets

2. compiler.hooks.afterEmit + compiler.hooks.done
   → Same as above
────────────────────────────────────────────────────────────────

Rspack Compatibility

Rspack ships a webpack-5-compatible Plugin API. This plugin works in Rspack projects out of the box:

// rspack.config.js
const { rspack } = require("@rspack/core");
const WebpackPluginSharp = require("webpack-plugin-sharp");

module.exports = {
  plugins: [
    new rspack.HtmlRspackPlugin({ template: "./index.html" }),
    // ⚠️ Use Rspack's built-in CopyRspackPlugin instead of copy-webpack-plugin
    new rspack.CopyRspackPlugin({ patterns: [{ from: "public", to: "." }] }),
    new WebpackPluginSharp(),
  ],
};

The plugin accesses sources and Compilation via compiler.webpack — the webpack 5 / Rspack-recommended pattern that avoids CJS / ESM named-import issues.

Using Vite?

If your project uses Vite, use the equivalent sister package instead:

npm install vite-plugin-sharp -D
// vite.config.ts
import VitePluginSharp from "vite-plugin-sharp";

export default {
  plugins: [
    VitePluginSharp(),
  ],
};

Both packages share the same options API (compress, resize, cache, include/exclude, etc.) and can be swapped with minimal changes.


Development

npm run build        # Build the plugin
npm run dev          # Watch mode
npm test             # Run tests
npm run test:watch   # Watch tests

License

ISC