Skip to content

Commit 770c2f2

Browse files
authored
feat: add support for webp image format (#25)
* feat: webp support * doc: updates README.md with webp
1 parent 9a9f372 commit 770c2f2

File tree

7 files changed

+33
-2
lines changed

7 files changed

+33
-2
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ The plugin supports the following compressors:
5555
- `ico`: For ICO images.
5656
- `svg`: For SVG images.
5757
- `avif`: For AVIF images.
58+
- `webp`: For WEBP images.
5859

5960
Only SVG are compressed by `svgo`, other compressors are compressed by `@napi-rs/image`.
6061

@@ -75,6 +76,8 @@ pluginImageCompress([
7576
{ use: "png", minQuality: 50 },
7677
// Options for @napi-rs/image `avif` method
7778
{ use: "avif", quality: 80 },
79+
// Options for @napi-rs/image `webp` method
80+
{ use: "webp", quality: 80 },
7881
// Options for svgo
7982
{ use: 'svg', floatPrecision: 2 }
8083
// No options yet
@@ -132,6 +135,11 @@ export default defineConfig({
132135
test: /\.avif$/,
133136
quality: 80,
134137
}),
138+
new ImageMinimizerPlugin({
139+
use: "webp",
140+
test: /\.webp$/,
141+
quality: 80,
142+
}),
135143
new ImageMinimizerPlugin({
136144
use: "svg",
137145
test: /\.svg$/,

src/shared/codecs.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ export const avifCodec: Codec<'avif'> = {
5353
},
5454
};
5555

56+
export const webpCodec: Codec<'webp'> = {
57+
async handler(buf, options) {
58+
return new Transformer(buf).webp(options.quality, options.signal);
59+
},
60+
defaultOptions: {
61+
test: /\.webp$/,
62+
},
63+
};
64+
5665
export const svgCodec: Codec<'svg'> = {
5766
async handler(buf, options) {
5867
const result = svgo.optimize(buf.toString(), options);
@@ -71,6 +80,7 @@ const codecs: Record<Codecs, Codec<any>> = {
7180
ico: icoCodec,
7281
svg: svgCodec,
7382
avif: avifCodec,
83+
webp: webpCodec,
7484
};
7585

7686
export default codecs;

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type OneOrMany<T> = T | T[];
1111

1212
export interface WebpTransformOptions {
1313
quality?: number;
14+
signal?: AbortSignal;
1415
}
1516

1617
export interface CodecBaseOptions {
@@ -20,6 +21,7 @@ export interface CodecBaseOptions {
2021
ico: Record<string, unknown>;
2122
svg: SvgoConfig;
2223
avif: AvifConfig;
24+
webp: WebpTransformOptions;
2325
}
2426

2527
export interface BaseCompressOptions<T extends Codecs> {

test/assets/image.webp

39.4 KB
Loading

test/basic/index.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,29 @@ test('should compress image with use plugin-image-compress', async () => {
3030
join(__dirname, 'dist/static/image/image.avif'),
3131
'utf-8',
3232
);
33+
34+
const webp = readFileSync(
35+
join(__dirname, 'dist/static/image/image.webp'),
36+
'utf-8',
37+
);
38+
3339
// const ico = names.find((item) => item.endsWith('.ico'))!;
3440

3541
const assetsDir = join(__dirname, '../assets');
3642
const originJpeg = readFileSync(join(assetsDir, 'image.jpeg'), 'utf-8');
3743
const originPng = readFileSync(join(assetsDir, 'image.png'), 'utf-8');
3844
const originSvg = readFileSync(join(assetsDir, 'mobile.svg'), 'utf-8');
3945
const originAvif = readFileSync(join(assetsDir, 'image.avif'), 'utf-8');
46+
const originWebp = readFileSync(join(assetsDir, 'image.webp'), 'utf-8');
47+
4048
// const originIco = readFileSync(join(assetsDir, 'image.ico'), 'utf-8');
4149

4250
expect(jpeg.length).toBeLessThan(originJpeg.length);
4351
expect(png.length).toBeLessThan(originPng.length);
4452
expect(svg.length).toBeLessThan(originSvg.length);
4553
expect(avif.length).toBeLessThan(originAvif.length);
54+
expect(webp.length).toBeLessThan(originWebp.length);
55+
4656
console.log(avif.length, originAvif.length);
4757
// TODO ico file size is not less than origin
4858
// expect(outputs[ico].length).toBeLessThan(originIco.length);

test/basic/rsbuild.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { defineConfig } from '@rsbuild/core';
22
import { pluginImageCompress } from '@rsbuild/plugin-image-compress';
33

44
export default defineConfig({
5-
plugins: [pluginImageCompress(['jpeg', 'png', 'ico', 'svg', 'avif'])],
5+
plugins: [pluginImageCompress(['jpeg', 'png', 'ico', 'svg', 'avif', 'webp'])],
66
output: {
77
filename: {
88
svg: '[name][ext]',

test/basic/src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import imageAvif from '../../assets/image.avif?url';
22
import imageIco from '../../assets/image.ico?url';
33
import imageJpeg from '../../assets/image.jpeg?url';
44
import imagePng from '../../assets/image.png?url';
5+
import imageWebp from '../../assets/image.webp?url';
56
import imageSvg from '../../assets/mobile.svg?url';
67

7-
const images = [imageIco, imagePng, imageJpeg, imageSvg, imageAvif];
8+
const images = [imageIco, imagePng, imageJpeg, imageSvg, imageAvif, imageWebp];
89

910
for (const image of images) {
1011
const el = new Image();

0 commit comments

Comments
 (0)