webpack-plugin-sharp
v1.0.2
Published
webpack / Rspack plugin for image compression using sharp and svgo
Downloads
178
Maintainers
Readme
webpack-plugin-sharp
中文
基于 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: 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 默认参数 |
| svg | svgo preset-default,multipass: 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访问sources和Compilation,这是 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(),
],
};两者配置项完全一致(compress、resize、cache、include/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 -DQuick 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/includefilters 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
sourcesandCompilationviacompiler.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 testsLicense
ISC
