@binarynoir/vite-plugin-optimize-images 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/index.cjs +132 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BinaryNoir
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# vite-plugin-optimize-images
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@binarynoir/vite-plugin-optimize-images)
|
|
4
|
+
[](https://github.com/binarynoir/vite-plugin-optimize-images/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Re-encodes PNG, JPEG, and WebP assets with [sharp](https://sharp.pixelplumbing.com)
|
|
8
|
+
as [Vite](https://vite.dev) writes them to the dist bundle — smaller images in
|
|
9
|
+
your build output with no change to your source files, no separate
|
|
10
|
+
build-your-own-assets step, and no manual re-compression before every commit.
|
|
11
|
+
|
|
12
|
+
- **Never modifies source files** — only the copies written to `dist/` are
|
|
13
|
+
touched.
|
|
14
|
+
- **Only optimizes what actually ships** — images that never make it into the
|
|
15
|
+
bundle are never processed.
|
|
16
|
+
- **Keeps the original if optimization doesn't help** — a re-encode is only
|
|
17
|
+
applied when it actually shrinks the file by a meaningful amount (see
|
|
18
|
+
`minSavings`).
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install --save-dev @binarynoir/vite-plugin-optimize-images
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// vite.config.ts
|
|
30
|
+
import { defineConfig } from "vite";
|
|
31
|
+
import { optimizeImagesPlugin } from "@binarynoir/vite-plugin-optimize-images";
|
|
32
|
+
|
|
33
|
+
export default defineConfig({
|
|
34
|
+
plugins: [optimizeImagesPlugin()],
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That's it — `.png`, `.jpg`/`.jpeg`, and `.webp` assets in your build output
|
|
39
|
+
get re-encoded automatically. Run a build with `VITE_OPTIMIZE_VERBOSE`-style
|
|
40
|
+
visibility by passing `verbose: true` (see below) to see what was optimized
|
|
41
|
+
and by how much.
|
|
42
|
+
|
|
43
|
+
## Options
|
|
44
|
+
|
|
45
|
+
| Option | Default | Description |
|
|
46
|
+
| ------------ | ------- | -------------------------------------------------------------------------------------- |
|
|
47
|
+
| `minSize` | `10240` | Minimum source size (bytes) before an image is considered for optimization (10 KiB). |
|
|
48
|
+
| `minSavings` | `1024` | Minimum bytes an optimization must save before it's applied (1 KiB). |
|
|
49
|
+
| `png` | — | sharp [PNG encode options](https://sharp.pixelplumbing.com/api-output#png) override. |
|
|
50
|
+
| `jpeg` | — | sharp [JPEG encode options](https://sharp.pixelplumbing.com/api-output#jpeg) override. |
|
|
51
|
+
| `webp` | — | sharp [WebP encode options](https://sharp.pixelplumbing.com/api-output#webp) override. |
|
|
52
|
+
| `verbose` | `false` | Log progress and per-file savings to the console. |
|
|
53
|
+
|
|
54
|
+
Defaults, before any override:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
{
|
|
58
|
+
png: { quality: 80, compressionLevel: 9, adaptiveFiltering: true },
|
|
59
|
+
jpeg: { quality: 85, progressive: true, mozjpeg: true },
|
|
60
|
+
webp: { quality: 85 },
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
optimizeImagesPlugin({
|
|
66
|
+
minSize: 5 * 1024,
|
|
67
|
+
minSavings: 512,
|
|
68
|
+
jpeg: { quality: 75 },
|
|
69
|
+
verbose: true,
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Why not `vite-plugin-imagemin` / `vite-imagetools` / etc.?
|
|
74
|
+
|
|
75
|
+
Those are great, more general options if you want format conversion, resizing,
|
|
76
|
+
or a wider codec set. This plugin is intentionally small: it re-encodes the
|
|
77
|
+
three formats sharp handles fastest, only ever operates on what's already in
|
|
78
|
+
the output bundle (so it composes cleanly with any other asset pipeline you
|
|
79
|
+
have), and never risks producing a _larger_ file than it started with.
|
|
80
|
+
|
|
81
|
+
## Releasing
|
|
82
|
+
|
|
83
|
+
Releases are tag-triggered. To ship a new version, from a clean `main` that's
|
|
84
|
+
in sync with `origin/main`:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npm run release:patch # or release:minor / release:major
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This runs typecheck/lint/test/build locally, then `npm version <bump>`
|
|
91
|
+
(bumps `package.json`, commits, and creates a matching `vX.Y.Z` tag) and
|
|
92
|
+
`git push --follow-tags`. Pushing that tag triggers
|
|
93
|
+
[`.github/workflows/release.yml`](.github/workflows/release.yml), which
|
|
94
|
+
re-runs the checks, publishes to npm (with
|
|
95
|
+
[provenance](https://docs.npmjs.com/generating-provenance-statements)), and
|
|
96
|
+
creates a GitHub release with auto-generated notes.
|
|
97
|
+
|
|
98
|
+
For a prerelease or an explicit version, use `npm run release -- <arg>`
|
|
99
|
+
(e.g. `npm run release -- 1.2.3` or `npm run release -- prerelease`) — see
|
|
100
|
+
[`npm version`](https://docs.npmjs.com/cli/v10/commands/npm-version) for the
|
|
101
|
+
full list of accepted values.
|
|
102
|
+
|
|
103
|
+
This requires an `NPM_TOKEN` repository secret (an npm
|
|
104
|
+
[automation token](https://docs.npmjs.com/creating-and-viewing-access-tokens)
|
|
105
|
+
with publish access) — set it under Settings → Secrets and variables →
|
|
106
|
+
Actions. First time publishing this package? See [PUBLISHING.md](PUBLISHING.md).
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
[MIT](LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
optimizeImagesPlugin: () => optimizeImagesPlugin
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/plugin.ts
|
|
38
|
+
var import_sharp = __toESM(require("sharp"), 1);
|
|
39
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
40
|
+
var DEFAULT_MIN_SIZE = 10 * 1024;
|
|
41
|
+
var DEFAULT_MIN_SAVINGS = 1024;
|
|
42
|
+
function optimizeImagesPlugin(options = {}) {
|
|
43
|
+
const minSize = options.minSize ?? DEFAULT_MIN_SIZE;
|
|
44
|
+
const minSavings = options.minSavings ?? DEFAULT_MIN_SAVINGS;
|
|
45
|
+
const verbose = options.verbose ?? false;
|
|
46
|
+
const pngOptions = { quality: 80, compressionLevel: 9, adaptiveFiltering: true, ...options.png };
|
|
47
|
+
const jpegOptions = { quality: 85, progressive: true, mozjpeg: true, ...options.jpeg };
|
|
48
|
+
const webpOptions = { quality: 85, ...options.webp };
|
|
49
|
+
return {
|
|
50
|
+
name: "vite-plugin-optimize-images",
|
|
51
|
+
enforce: "post",
|
|
52
|
+
// Run after other plugins
|
|
53
|
+
async generateBundle(_outputOptions, bundle) {
|
|
54
|
+
let optimizedCount = 0;
|
|
55
|
+
let totalSavings = 0;
|
|
56
|
+
let totalOriginalSize = 0;
|
|
57
|
+
const imageAssets = Object.entries(bundle).filter(
|
|
58
|
+
([fileName, asset]) => asset && asset.type === "asset" && typeof asset.source !== "string" && fileName.match(/\.(jpg|jpeg|png|webp)$/i) && asset.source.length >= minSize
|
|
59
|
+
);
|
|
60
|
+
if (imageAssets.length === 0) {
|
|
61
|
+
if (verbose) {
|
|
62
|
+
console.log("[vite-plugin-optimize-images] No images found in bundle");
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (verbose) {
|
|
67
|
+
console.log(
|
|
68
|
+
`[vite-plugin-optimize-images] Found ${imageAssets.length} images to process...`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
for (const [fileName, bundleAsset] of imageAssets) {
|
|
72
|
+
if (bundleAsset.type !== "asset" || typeof bundleAsset.source === "string") {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const asset = bundleAsset;
|
|
76
|
+
const originalSize = asset.source.length;
|
|
77
|
+
const originalBuffer = Buffer.from(asset.source);
|
|
78
|
+
try {
|
|
79
|
+
const ext = import_node_path.default.extname(fileName).toLowerCase();
|
|
80
|
+
let optimizedBuffer;
|
|
81
|
+
if (ext === ".png") {
|
|
82
|
+
optimizedBuffer = await (0, import_sharp.default)(originalBuffer).png(pngOptions).toBuffer();
|
|
83
|
+
} else if (ext === ".jpg" || ext === ".jpeg") {
|
|
84
|
+
optimizedBuffer = await (0, import_sharp.default)(originalBuffer).jpeg(jpegOptions).toBuffer();
|
|
85
|
+
} else if (ext === ".webp") {
|
|
86
|
+
optimizedBuffer = await (0, import_sharp.default)(originalBuffer).webp(webpOptions).toBuffer();
|
|
87
|
+
} else {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const optimizedSize = optimizedBuffer.length;
|
|
91
|
+
const savings = originalSize - optimizedSize;
|
|
92
|
+
const savingsPercent = (savings / originalSize * 100).toFixed(1);
|
|
93
|
+
if (savings > minSavings) {
|
|
94
|
+
asset.source = optimizedBuffer;
|
|
95
|
+
optimizedCount++;
|
|
96
|
+
totalSavings += savings;
|
|
97
|
+
totalOriginalSize += originalSize;
|
|
98
|
+
if (verbose) {
|
|
99
|
+
const originalKB = (originalSize / 1024).toFixed(2);
|
|
100
|
+
const optimizedKB = (optimizedSize / 1024).toFixed(2);
|
|
101
|
+
console.log(
|
|
102
|
+
`[vite-plugin-optimize-images] \u2713 ${fileName}: ${originalKB} KB \u2192 ${optimizedKB} KB (-${savingsPercent}%)`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
108
|
+
console.warn(`[vite-plugin-optimize-images] Failed to optimize ${fileName}:`, errorMsg);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (optimizedCount > 0) {
|
|
112
|
+
const totalOriginalMB = (totalOriginalSize / (1024 * 1024)).toFixed(2);
|
|
113
|
+
const totalOptimizedSize = totalOriginalSize - totalSavings;
|
|
114
|
+
const totalOptimizedMB = (totalOptimizedSize / (1024 * 1024)).toFixed(2);
|
|
115
|
+
const totalSavingsMB = (totalSavings / (1024 * 1024)).toFixed(2);
|
|
116
|
+
const savingsPercent = (totalSavings / totalOriginalSize * 100).toFixed(1);
|
|
117
|
+
console.log(`[vite-plugin-optimize-images] Optimization complete:`);
|
|
118
|
+
console.log(` Optimized: ${optimizedCount} images`);
|
|
119
|
+
console.log(` Original size: ${totalOriginalMB} MB`);
|
|
120
|
+
console.log(` Optimized size: ${totalOptimizedMB} MB`);
|
|
121
|
+
console.log(` Total savings: ${totalSavingsMB} MB (-${savingsPercent}%)`);
|
|
122
|
+
} else {
|
|
123
|
+
console.log("[vite-plugin-optimize-images] No images required optimization");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
129
|
+
0 && (module.exports = {
|
|
130
|
+
optimizeImagesPlugin
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/plugin.ts"],"sourcesContent":["export { optimizeImagesPlugin } from \"./plugin.js\";\nexport type {\n OptimizeImagesOptions,\n PngEncodeOptions,\n JpegEncodeOptions,\n WebpEncodeOptions,\n} from \"./types.js\";\n","import type { Plugin } from \"vite\";\nimport sharp from \"sharp\";\nimport path from \"node:path\";\nimport type { OptimizeImagesOptions } from \"./types.js\";\n\nconst DEFAULT_MIN_SIZE = 10 * 1024;\nconst DEFAULT_MIN_SAVINGS = 1024;\n\n/**\n * Vite plugin that optimizes images as they're written to the dist bundle.\n * - Never modifies source files\n * - Optimizes only images that actually make it to dist\n */\nexport function optimizeImagesPlugin(options: OptimizeImagesOptions = {}): Plugin {\n const minSize = options.minSize ?? DEFAULT_MIN_SIZE;\n const minSavings = options.minSavings ?? DEFAULT_MIN_SAVINGS;\n const verbose = options.verbose ?? false;\n const pngOptions = { quality: 80, compressionLevel: 9, adaptiveFiltering: true, ...options.png };\n const jpegOptions = { quality: 85, progressive: true, mozjpeg: true, ...options.jpeg };\n const webpOptions = { quality: 85, ...options.webp };\n\n return {\n name: \"vite-plugin-optimize-images\",\n enforce: \"post\", // Run after other plugins\n\n async generateBundle(_outputOptions, bundle) {\n let optimizedCount = 0;\n let totalSavings = 0;\n let totalOriginalSize = 0;\n\n // Count image assets first to avoid premature \"no images\" message\n const imageAssets = Object.entries(bundle).filter(\n ([fileName, asset]) =>\n asset &&\n asset.type === \"asset\" &&\n typeof asset.source !== \"string\" &&\n fileName.match(/\\.(jpg|jpeg|png|webp)$/i) &&\n asset.source.length >= minSize,\n );\n\n // Skip processing if no eligible images\n if (imageAssets.length === 0) {\n if (verbose) {\n console.log(\"[vite-plugin-optimize-images] No images found in bundle\");\n }\n return;\n }\n\n if (verbose) {\n console.log(\n `[vite-plugin-optimize-images] Found ${imageAssets.length} images to process...`,\n );\n }\n\n // Process all eligible image assets\n for (const [fileName, bundleAsset] of imageAssets) {\n // Type guard: we already filtered for assets with source\n if (bundleAsset.type !== \"asset\" || typeof bundleAsset.source === \"string\") {\n continue;\n }\n\n const asset = bundleAsset;\n const originalSize = asset.source.length;\n const originalBuffer = Buffer.from(asset.source);\n\n try {\n // Optimize based on file type\n const ext = path.extname(fileName).toLowerCase();\n let optimizedBuffer: Buffer;\n\n if (ext === \".png\") {\n optimizedBuffer = await sharp(originalBuffer).png(pngOptions).toBuffer();\n } else if (ext === \".jpg\" || ext === \".jpeg\") {\n optimizedBuffer = await sharp(originalBuffer).jpeg(jpegOptions).toBuffer();\n } else if (ext === \".webp\") {\n optimizedBuffer = await sharp(originalBuffer).webp(webpOptions).toBuffer();\n } else {\n continue;\n }\n\n // Only use optimized version if it's actually smaller\n const optimizedSize = optimizedBuffer.length;\n const savings = originalSize - optimizedSize;\n const savingsPercent = ((savings / originalSize) * 100).toFixed(1);\n\n if (savings > minSavings) {\n asset.source = optimizedBuffer;\n optimizedCount++;\n totalSavings += savings;\n totalOriginalSize += originalSize;\n\n if (verbose) {\n const originalKB = (originalSize / 1024).toFixed(2);\n const optimizedKB = (optimizedSize / 1024).toFixed(2);\n console.log(\n `[vite-plugin-optimize-images] ✓ ${fileName}: ${originalKB} KB → ${optimizedKB} KB (-${savingsPercent}%)`,\n );\n }\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n console.warn(`[vite-plugin-optimize-images] Failed to optimize ${fileName}:`, errorMsg);\n }\n }\n\n // Show final summary after processing all images\n if (optimizedCount > 0) {\n const totalOriginalMB = (totalOriginalSize / (1024 * 1024)).toFixed(2);\n const totalOptimizedSize = totalOriginalSize - totalSavings;\n const totalOptimizedMB = (totalOptimizedSize / (1024 * 1024)).toFixed(2);\n const totalSavingsMB = (totalSavings / (1024 * 1024)).toFixed(2);\n const savingsPercent = ((totalSavings / totalOriginalSize) * 100).toFixed(1);\n\n console.log(`[vite-plugin-optimize-images] Optimization complete:`);\n console.log(` Optimized: ${optimizedCount} images`);\n console.log(` Original size: ${totalOriginalMB} MB`);\n console.log(` Optimized size: ${totalOptimizedMB} MB`);\n console.log(` Total savings: ${totalSavingsMB} MB (-${savingsPercent}%)`);\n } else {\n console.log(\"[vite-plugin-optimize-images] No images required optimization\");\n }\n },\n };\n}\n"],"mappings":"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","names":["path","sharp"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface PngEncodeOptions {
|
|
4
|
+
quality?: number;
|
|
5
|
+
compressionLevel?: number;
|
|
6
|
+
adaptiveFiltering?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface JpegEncodeOptions {
|
|
9
|
+
quality?: number;
|
|
10
|
+
progressive?: boolean;
|
|
11
|
+
mozjpeg?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface WebpEncodeOptions {
|
|
14
|
+
quality?: number;
|
|
15
|
+
}
|
|
16
|
+
interface OptimizeImagesOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Minimum source size (bytes) before an image is considered for
|
|
19
|
+
* optimization.
|
|
20
|
+
* @default 10240 (10 KiB)
|
|
21
|
+
*/
|
|
22
|
+
minSize?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Minimum bytes an optimization must save before it's applied — a
|
|
25
|
+
* re-encode that doesn't clear this bar is discarded and the original
|
|
26
|
+
* bytes are kept.
|
|
27
|
+
* @default 1024 (1 KiB)
|
|
28
|
+
*/
|
|
29
|
+
minSavings?: number;
|
|
30
|
+
/** sharp PNG encode options. */
|
|
31
|
+
png?: PngEncodeOptions;
|
|
32
|
+
/** sharp JPEG encode options. */
|
|
33
|
+
jpeg?: JpegEncodeOptions;
|
|
34
|
+
/** sharp WebP encode options. */
|
|
35
|
+
webp?: WebpEncodeOptions;
|
|
36
|
+
/**
|
|
37
|
+
* Log progress and per-file savings to the console.
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
verbose?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Vite plugin that optimizes images as they're written to the dist bundle.
|
|
45
|
+
* - Never modifies source files
|
|
46
|
+
* - Optimizes only images that actually make it to dist
|
|
47
|
+
*/
|
|
48
|
+
declare function optimizeImagesPlugin(options?: OptimizeImagesOptions): Plugin;
|
|
49
|
+
|
|
50
|
+
export { type JpegEncodeOptions, type OptimizeImagesOptions, type PngEncodeOptions, type WebpEncodeOptions, optimizeImagesPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface PngEncodeOptions {
|
|
4
|
+
quality?: number;
|
|
5
|
+
compressionLevel?: number;
|
|
6
|
+
adaptiveFiltering?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface JpegEncodeOptions {
|
|
9
|
+
quality?: number;
|
|
10
|
+
progressive?: boolean;
|
|
11
|
+
mozjpeg?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface WebpEncodeOptions {
|
|
14
|
+
quality?: number;
|
|
15
|
+
}
|
|
16
|
+
interface OptimizeImagesOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Minimum source size (bytes) before an image is considered for
|
|
19
|
+
* optimization.
|
|
20
|
+
* @default 10240 (10 KiB)
|
|
21
|
+
*/
|
|
22
|
+
minSize?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Minimum bytes an optimization must save before it's applied — a
|
|
25
|
+
* re-encode that doesn't clear this bar is discarded and the original
|
|
26
|
+
* bytes are kept.
|
|
27
|
+
* @default 1024 (1 KiB)
|
|
28
|
+
*/
|
|
29
|
+
minSavings?: number;
|
|
30
|
+
/** sharp PNG encode options. */
|
|
31
|
+
png?: PngEncodeOptions;
|
|
32
|
+
/** sharp JPEG encode options. */
|
|
33
|
+
jpeg?: JpegEncodeOptions;
|
|
34
|
+
/** sharp WebP encode options. */
|
|
35
|
+
webp?: WebpEncodeOptions;
|
|
36
|
+
/**
|
|
37
|
+
* Log progress and per-file savings to the console.
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
verbose?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Vite plugin that optimizes images as they're written to the dist bundle.
|
|
45
|
+
* - Never modifies source files
|
|
46
|
+
* - Optimizes only images that actually make it to dist
|
|
47
|
+
*/
|
|
48
|
+
declare function optimizeImagesPlugin(options?: OptimizeImagesOptions): Plugin;
|
|
49
|
+
|
|
50
|
+
export { type JpegEncodeOptions, type OptimizeImagesOptions, type PngEncodeOptions, type WebpEncodeOptions, optimizeImagesPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
import sharp from "sharp";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var DEFAULT_MIN_SIZE = 10 * 1024;
|
|
5
|
+
var DEFAULT_MIN_SAVINGS = 1024;
|
|
6
|
+
function optimizeImagesPlugin(options = {}) {
|
|
7
|
+
const minSize = options.minSize ?? DEFAULT_MIN_SIZE;
|
|
8
|
+
const minSavings = options.minSavings ?? DEFAULT_MIN_SAVINGS;
|
|
9
|
+
const verbose = options.verbose ?? false;
|
|
10
|
+
const pngOptions = { quality: 80, compressionLevel: 9, adaptiveFiltering: true, ...options.png };
|
|
11
|
+
const jpegOptions = { quality: 85, progressive: true, mozjpeg: true, ...options.jpeg };
|
|
12
|
+
const webpOptions = { quality: 85, ...options.webp };
|
|
13
|
+
return {
|
|
14
|
+
name: "vite-plugin-optimize-images",
|
|
15
|
+
enforce: "post",
|
|
16
|
+
// Run after other plugins
|
|
17
|
+
async generateBundle(_outputOptions, bundle) {
|
|
18
|
+
let optimizedCount = 0;
|
|
19
|
+
let totalSavings = 0;
|
|
20
|
+
let totalOriginalSize = 0;
|
|
21
|
+
const imageAssets = Object.entries(bundle).filter(
|
|
22
|
+
([fileName, asset]) => asset && asset.type === "asset" && typeof asset.source !== "string" && fileName.match(/\.(jpg|jpeg|png|webp)$/i) && asset.source.length >= minSize
|
|
23
|
+
);
|
|
24
|
+
if (imageAssets.length === 0) {
|
|
25
|
+
if (verbose) {
|
|
26
|
+
console.log("[vite-plugin-optimize-images] No images found in bundle");
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (verbose) {
|
|
31
|
+
console.log(
|
|
32
|
+
`[vite-plugin-optimize-images] Found ${imageAssets.length} images to process...`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
for (const [fileName, bundleAsset] of imageAssets) {
|
|
36
|
+
if (bundleAsset.type !== "asset" || typeof bundleAsset.source === "string") {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const asset = bundleAsset;
|
|
40
|
+
const originalSize = asset.source.length;
|
|
41
|
+
const originalBuffer = Buffer.from(asset.source);
|
|
42
|
+
try {
|
|
43
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
44
|
+
let optimizedBuffer;
|
|
45
|
+
if (ext === ".png") {
|
|
46
|
+
optimizedBuffer = await sharp(originalBuffer).png(pngOptions).toBuffer();
|
|
47
|
+
} else if (ext === ".jpg" || ext === ".jpeg") {
|
|
48
|
+
optimizedBuffer = await sharp(originalBuffer).jpeg(jpegOptions).toBuffer();
|
|
49
|
+
} else if (ext === ".webp") {
|
|
50
|
+
optimizedBuffer = await sharp(originalBuffer).webp(webpOptions).toBuffer();
|
|
51
|
+
} else {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const optimizedSize = optimizedBuffer.length;
|
|
55
|
+
const savings = originalSize - optimizedSize;
|
|
56
|
+
const savingsPercent = (savings / originalSize * 100).toFixed(1);
|
|
57
|
+
if (savings > minSavings) {
|
|
58
|
+
asset.source = optimizedBuffer;
|
|
59
|
+
optimizedCount++;
|
|
60
|
+
totalSavings += savings;
|
|
61
|
+
totalOriginalSize += originalSize;
|
|
62
|
+
if (verbose) {
|
|
63
|
+
const originalKB = (originalSize / 1024).toFixed(2);
|
|
64
|
+
const optimizedKB = (optimizedSize / 1024).toFixed(2);
|
|
65
|
+
console.log(
|
|
66
|
+
`[vite-plugin-optimize-images] \u2713 ${fileName}: ${originalKB} KB \u2192 ${optimizedKB} KB (-${savingsPercent}%)`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
72
|
+
console.warn(`[vite-plugin-optimize-images] Failed to optimize ${fileName}:`, errorMsg);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (optimizedCount > 0) {
|
|
76
|
+
const totalOriginalMB = (totalOriginalSize / (1024 * 1024)).toFixed(2);
|
|
77
|
+
const totalOptimizedSize = totalOriginalSize - totalSavings;
|
|
78
|
+
const totalOptimizedMB = (totalOptimizedSize / (1024 * 1024)).toFixed(2);
|
|
79
|
+
const totalSavingsMB = (totalSavings / (1024 * 1024)).toFixed(2);
|
|
80
|
+
const savingsPercent = (totalSavings / totalOriginalSize * 100).toFixed(1);
|
|
81
|
+
console.log(`[vite-plugin-optimize-images] Optimization complete:`);
|
|
82
|
+
console.log(` Optimized: ${optimizedCount} images`);
|
|
83
|
+
console.log(` Original size: ${totalOriginalMB} MB`);
|
|
84
|
+
console.log(` Optimized size: ${totalOptimizedMB} MB`);
|
|
85
|
+
console.log(` Total savings: ${totalSavingsMB} MB (-${savingsPercent}%)`);
|
|
86
|
+
} else {
|
|
87
|
+
console.log("[vite-plugin-optimize-images] No images required optimization");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
optimizeImagesPlugin
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport sharp from \"sharp\";\nimport path from \"node:path\";\nimport type { OptimizeImagesOptions } from \"./types.js\";\n\nconst DEFAULT_MIN_SIZE = 10 * 1024;\nconst DEFAULT_MIN_SAVINGS = 1024;\n\n/**\n * Vite plugin that optimizes images as they're written to the dist bundle.\n * - Never modifies source files\n * - Optimizes only images that actually make it to dist\n */\nexport function optimizeImagesPlugin(options: OptimizeImagesOptions = {}): Plugin {\n const minSize = options.minSize ?? DEFAULT_MIN_SIZE;\n const minSavings = options.minSavings ?? DEFAULT_MIN_SAVINGS;\n const verbose = options.verbose ?? false;\n const pngOptions = { quality: 80, compressionLevel: 9, adaptiveFiltering: true, ...options.png };\n const jpegOptions = { quality: 85, progressive: true, mozjpeg: true, ...options.jpeg };\n const webpOptions = { quality: 85, ...options.webp };\n\n return {\n name: \"vite-plugin-optimize-images\",\n enforce: \"post\", // Run after other plugins\n\n async generateBundle(_outputOptions, bundle) {\n let optimizedCount = 0;\n let totalSavings = 0;\n let totalOriginalSize = 0;\n\n // Count image assets first to avoid premature \"no images\" message\n const imageAssets = Object.entries(bundle).filter(\n ([fileName, asset]) =>\n asset &&\n asset.type === \"asset\" &&\n typeof asset.source !== \"string\" &&\n fileName.match(/\\.(jpg|jpeg|png|webp)$/i) &&\n asset.source.length >= minSize,\n );\n\n // Skip processing if no eligible images\n if (imageAssets.length === 0) {\n if (verbose) {\n console.log(\"[vite-plugin-optimize-images] No images found in bundle\");\n }\n return;\n }\n\n if (verbose) {\n console.log(\n `[vite-plugin-optimize-images] Found ${imageAssets.length} images to process...`,\n );\n }\n\n // Process all eligible image assets\n for (const [fileName, bundleAsset] of imageAssets) {\n // Type guard: we already filtered for assets with source\n if (bundleAsset.type !== \"asset\" || typeof bundleAsset.source === \"string\") {\n continue;\n }\n\n const asset = bundleAsset;\n const originalSize = asset.source.length;\n const originalBuffer = Buffer.from(asset.source);\n\n try {\n // Optimize based on file type\n const ext = path.extname(fileName).toLowerCase();\n let optimizedBuffer: Buffer;\n\n if (ext === \".png\") {\n optimizedBuffer = await sharp(originalBuffer).png(pngOptions).toBuffer();\n } else if (ext === \".jpg\" || ext === \".jpeg\") {\n optimizedBuffer = await sharp(originalBuffer).jpeg(jpegOptions).toBuffer();\n } else if (ext === \".webp\") {\n optimizedBuffer = await sharp(originalBuffer).webp(webpOptions).toBuffer();\n } else {\n continue;\n }\n\n // Only use optimized version if it's actually smaller\n const optimizedSize = optimizedBuffer.length;\n const savings = originalSize - optimizedSize;\n const savingsPercent = ((savings / originalSize) * 100).toFixed(1);\n\n if (savings > minSavings) {\n asset.source = optimizedBuffer;\n optimizedCount++;\n totalSavings += savings;\n totalOriginalSize += originalSize;\n\n if (verbose) {\n const originalKB = (originalSize / 1024).toFixed(2);\n const optimizedKB = (optimizedSize / 1024).toFixed(2);\n console.log(\n `[vite-plugin-optimize-images] ✓ ${fileName}: ${originalKB} KB → ${optimizedKB} KB (-${savingsPercent}%)`,\n );\n }\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n console.warn(`[vite-plugin-optimize-images] Failed to optimize ${fileName}:`, errorMsg);\n }\n }\n\n // Show final summary after processing all images\n if (optimizedCount > 0) {\n const totalOriginalMB = (totalOriginalSize / (1024 * 1024)).toFixed(2);\n const totalOptimizedSize = totalOriginalSize - totalSavings;\n const totalOptimizedMB = (totalOptimizedSize / (1024 * 1024)).toFixed(2);\n const totalSavingsMB = (totalSavings / (1024 * 1024)).toFixed(2);\n const savingsPercent = ((totalSavings / totalOriginalSize) * 100).toFixed(1);\n\n console.log(`[vite-plugin-optimize-images] Optimization complete:`);\n console.log(` Optimized: ${optimizedCount} images`);\n console.log(` Original size: ${totalOriginalMB} MB`);\n console.log(` Optimized size: ${totalOptimizedMB} MB`);\n console.log(` Total savings: ${totalSavingsMB} MB (-${savingsPercent}%)`);\n } else {\n console.log(\"[vite-plugin-optimize-images] No images required optimization\");\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@binarynoir/vite-plugin-optimize-images",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A Vite plugin that optimizes PNG, JPEG, and WebP images as they're written to the dist bundle, using sharp — without ever touching your source files.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"vite",
|
|
7
|
+
"vite-plugin",
|
|
8
|
+
"images",
|
|
9
|
+
"image-optimization",
|
|
10
|
+
"sharp",
|
|
11
|
+
"build",
|
|
12
|
+
"performance"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "BinaryNoir",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/binarynoir/vite-plugin-optimize-images.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/binarynoir/vite-plugin-optimize-images#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/binarynoir/vite-plugin-optimize-images/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"main": "./dist/index.cjs",
|
|
27
|
+
"module": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"require": "./dist/index.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"dev": "tsup --watch",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"lint": "eslint .",
|
|
53
|
+
"lint:fix": "eslint . --fix",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"format:check": "prettier --check .",
|
|
56
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build",
|
|
57
|
+
"release": "node scripts/release.js",
|
|
58
|
+
"release:patch": "node scripts/release.js patch",
|
|
59
|
+
"release:minor": "node scripts/release.js minor",
|
|
60
|
+
"release:major": "node scripts/release.js major"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"sharp": "^0.35.4"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@types/node": "^26.5.1",
|
|
70
|
+
"eslint": "^10.10.0",
|
|
71
|
+
"eslint-config-prettier": "^10.1.8",
|
|
72
|
+
"prettier": "^3.9.6",
|
|
73
|
+
"tsup": "^8.3.5",
|
|
74
|
+
"typescript": "^5.9.2",
|
|
75
|
+
"typescript-eslint": "^8.19.0",
|
|
76
|
+
"vite": "^8.3.0",
|
|
77
|
+
"vitest": "^5.0.1"
|
|
78
|
+
},
|
|
79
|
+
"allowScripts": {
|
|
80
|
+
"esbuild@0.27.7": true
|
|
81
|
+
}
|
|
82
|
+
}
|