@rsbuild/plugin-image-compress 1.0.2 → 1.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/README.md +34 -9
- package/dist/index.cjs +238 -211
- package/dist/index.d.ts +7 -34
- package/dist/index.js +181 -182
- package/dist/minimizer.d.ts +13 -0
- package/dist/shared/codecs.d.ts +9 -0
- package/dist/shared/utils.d.ts +2 -0
- package/dist/types.d.ts +34 -0
- package/package.json +21 -20
- package/dist/index.d.cts +0 -38
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ With the image compression plugin, image assets used in the project can be compr
|
|
|
9
9
|
<img src="https://img.shields.io/npm/v/@rsbuild/plugin-image-compress?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" />
|
|
10
10
|
</a>
|
|
11
11
|
<img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" />
|
|
12
|
+
<a href="https://npmcharts.com/compare/@rsbuild/plugin-image-compress?minimal=true"><img src="https://img.shields.io/npm/dm/@rsbuild/plugin-image-compress.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="downloads" /></a>
|
|
12
13
|
</p>
|
|
13
14
|
|
|
14
15
|
## Usage
|
|
@@ -30,10 +31,7 @@ export default {
|
|
|
30
31
|
};
|
|
31
32
|
```
|
|
32
33
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
The plugin accepts an array of compressor configuration options, each of which can be either a string or an object. The string can be the name of a built-in compressor and its default configuration enabled.
|
|
36
|
-
Or use the object format configuration and specify the compressor in the `use` field. The remaining fields of the object will be used as compressor configuration options.
|
|
34
|
+
## Default Compressors
|
|
37
35
|
|
|
38
36
|
By default, the plugin will enable `jpeg`, `png`, `ico` image compressors, which are equivalent to the following two examples:
|
|
39
37
|
|
|
@@ -45,19 +43,48 @@ pluginImageCompress(["jpeg", "png", "ico"]);
|
|
|
45
43
|
pluginImageCompress([{ use: "jpeg" }, { use: "png" }, { use: "ico" }]);
|
|
46
44
|
```
|
|
47
45
|
|
|
46
|
+
## Supported Compressors
|
|
47
|
+
|
|
48
|
+
The plugin supports the following compressors:
|
|
49
|
+
|
|
50
|
+
- `jpeg`: For JPEG images.
|
|
51
|
+
- `png`: For PNG images.
|
|
52
|
+
- `pngLossless`: For PNG images with lossless compression.
|
|
53
|
+
- `ico`: For ICO images.
|
|
54
|
+
- `svg`: For SVG images.
|
|
55
|
+
- `avif`: For AVIF images.
|
|
56
|
+
|
|
57
|
+
Only SVG are compressed by `svgo`, other compressors are compressed by `@napi-rs/image`.
|
|
58
|
+
|
|
59
|
+
## Options
|
|
60
|
+
|
|
61
|
+
The plugin accepts an array of compressor configuration options, each of which can be either a string or an object. The string can be the name of a built-in compressor and its default configuration enabled.
|
|
62
|
+
|
|
63
|
+
Or use the object format configuration and specify the compressor in the `use` field. The remaining fields of the object will be used as compressor configuration options.
|
|
64
|
+
|
|
48
65
|
The default configuration can be overridden by specifying a configuration option.
|
|
49
66
|
For example, to allow the jpeg compressor to recognize new extension name and to set the quality of the png compressor.
|
|
50
67
|
|
|
51
68
|
```js
|
|
52
69
|
pluginImageCompress([
|
|
70
|
+
// Options for @napi-rs/image `compressJpeg` method
|
|
53
71
|
{ use: "jpeg", test: /\.(?:jpg|jpeg|jpe)$/ },
|
|
72
|
+
// Options for @napi-rs/image `pngQuantize` method
|
|
54
73
|
{ use: "png", minQuality: 50 },
|
|
55
|
-
|
|
74
|
+
// Options for @napi-rs/image `avif` method
|
|
75
|
+
{ use: "avif", quality: 80 },
|
|
76
|
+
// Options for svgo
|
|
77
|
+
{ use: 'svg', floatPrecision: 2 }
|
|
78
|
+
// No options yet
|
|
79
|
+
{ use: "ico" },
|
|
56
80
|
]);
|
|
57
81
|
```
|
|
58
82
|
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
For more information on compressors, please visit [@napi-rs/image](https://image.napi.rs/docs).
|
|
84
|
+
|
|
85
|
+
## Lossless PNG
|
|
86
|
+
|
|
87
|
+
The default `png` compressor is lossy. If you want to replace it with a lossless compressor, you can use the following configuration.
|
|
61
88
|
|
|
62
89
|
```js
|
|
63
90
|
pluginImageCompress(["jpeg", "pngLossless", "ico"]);
|
|
@@ -71,8 +98,6 @@ For example, the `png` compressor will take precedence over the `pngLossless` co
|
|
|
71
98
|
pluginImageCompress(["jpeg", "pngLossless", "ico", "png"]);
|
|
72
99
|
```
|
|
73
100
|
|
|
74
|
-
For more information on compressors, please visit [@napi-rs/image](https://image.napi.rs/docs).
|
|
75
|
-
|
|
76
101
|
## License
|
|
77
102
|
|
|
78
103
|
[MIT](./LICENSE).
|
package/dist/index.cjs
CHANGED
|
@@ -1,226 +1,253 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
));
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
2
|
+
// The require scope
|
|
3
|
+
var __webpack_require__ = {};
|
|
4
|
+
/************************************************************************/ // webpack/runtime/compat_get_default_export
|
|
5
|
+
(()=>{
|
|
6
|
+
// getDefaultExport function for compatibility with non-ESM modules
|
|
7
|
+
__webpack_require__.n = function(module) {
|
|
8
|
+
var getter = module && module.__esModule ? function() {
|
|
9
|
+
return module['default'];
|
|
10
|
+
} : function() {
|
|
11
|
+
return module;
|
|
12
|
+
};
|
|
13
|
+
__webpack_require__.d(getter, {
|
|
14
|
+
a: getter
|
|
15
|
+
});
|
|
16
|
+
return getter;
|
|
17
|
+
};
|
|
18
|
+
})();
|
|
19
|
+
// webpack/runtime/define_property_getters
|
|
20
|
+
(()=>{
|
|
21
|
+
__webpack_require__.d = function(exports1, definition) {
|
|
22
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
get: definition[key]
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
// webpack/runtime/has_own_property
|
|
29
|
+
(()=>{
|
|
30
|
+
__webpack_require__.o = function(obj, prop) {
|
|
31
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
32
|
+
};
|
|
33
|
+
})();
|
|
34
|
+
// webpack/runtime/make_namespace_object
|
|
35
|
+
(()=>{
|
|
36
|
+
// define __esModule on exports
|
|
37
|
+
__webpack_require__.r = function(exports1) {
|
|
38
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
39
|
+
value: 'Module'
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
42
|
+
value: true
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
/************************************************************************/ var __webpack_exports__ = {};
|
|
47
|
+
// ESM COMPAT FLAG
|
|
48
|
+
__webpack_require__.r(__webpack_exports__);
|
|
49
|
+
// EXPORTS
|
|
50
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
51
|
+
DEFAULT_OPTIONS: ()=>/* binding */ DEFAULT_OPTIONS,
|
|
52
|
+
pluginImageCompress: ()=>/* binding */ pluginImageCompress,
|
|
53
|
+
PLUGIN_IMAGE_COMPRESS_NAME: ()=>/* binding */ PLUGIN_IMAGE_COMPRESS_NAME
|
|
36
54
|
});
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
defaultOptions: {
|
|
52
|
-
test: /\.(?:jpg|jpeg)$/
|
|
53
|
-
}
|
|
55
|
+
const external_node_assert_namespaceObject = require("node:assert");
|
|
56
|
+
var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
|
|
57
|
+
const external_node_buffer_namespaceObject = require("node:buffer");
|
|
58
|
+
const image_namespaceObject = require("@napi-rs/image");
|
|
59
|
+
const external_svgo_namespaceObject = require("svgo");
|
|
60
|
+
var external_svgo_default = /*#__PURE__*/ __webpack_require__.n(external_svgo_namespaceObject);
|
|
61
|
+
const jpegCodec = {
|
|
62
|
+
handler (buf, options) {
|
|
63
|
+
return (0, image_namespaceObject.compressJpeg)(buf, options);
|
|
64
|
+
},
|
|
65
|
+
defaultOptions: {
|
|
66
|
+
test: /\.(?:jpg|jpeg)$/
|
|
67
|
+
}
|
|
54
68
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
const pngCodec = {
|
|
70
|
+
handler (buf, options) {
|
|
71
|
+
return (0, image_namespaceObject.pngQuantize)(buf, options);
|
|
72
|
+
},
|
|
73
|
+
defaultOptions: {
|
|
74
|
+
test: /\.png$/
|
|
75
|
+
}
|
|
62
76
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
const pngLosslessCodec = {
|
|
78
|
+
handler (buf, options) {
|
|
79
|
+
return (0, image_namespaceObject.losslessCompressPng)(buf, options);
|
|
80
|
+
},
|
|
81
|
+
defaultOptions: {
|
|
82
|
+
test: /\.png$/
|
|
83
|
+
}
|
|
70
84
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
const icoCodec = {
|
|
86
|
+
handler (buf) {
|
|
87
|
+
return new image_namespaceObject.Transformer(buf).ico();
|
|
88
|
+
},
|
|
89
|
+
defaultOptions: {
|
|
90
|
+
test: /\.(?:ico|icon)$/
|
|
91
|
+
}
|
|
78
92
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
93
|
+
const avifCodec = {
|
|
94
|
+
handler (buf, options) {
|
|
95
|
+
return new image_namespaceObject.Transformer(buf).avif(options);
|
|
96
|
+
},
|
|
97
|
+
defaultOptions: {
|
|
98
|
+
test: /\.avif$/
|
|
99
|
+
}
|
|
87
100
|
};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
const svgCodec = {
|
|
102
|
+
async handler (buf, options) {
|
|
103
|
+
const result = external_svgo_default().optimize(buf.toString(), options);
|
|
104
|
+
return external_node_buffer_namespaceObject.Buffer.from(result.data);
|
|
105
|
+
},
|
|
106
|
+
defaultOptions: {
|
|
107
|
+
test: /\.svg$/
|
|
108
|
+
}
|
|
94
109
|
};
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
buildError
|
|
122
|
-
|
|
110
|
+
// biome-ignore lint/suspicious/noExplicitAny:allow any
|
|
111
|
+
const codecs = {
|
|
112
|
+
jpeg: jpegCodec,
|
|
113
|
+
png: pngCodec,
|
|
114
|
+
pngLossless: pngLosslessCodec,
|
|
115
|
+
ico: icoCodec,
|
|
116
|
+
svg: svgCodec,
|
|
117
|
+
avif: avifCodec
|
|
118
|
+
};
|
|
119
|
+
/* ESM default export */ const shared_codecs = codecs;
|
|
120
|
+
function _define_property(obj, key, value) {
|
|
121
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
122
|
+
value: value,
|
|
123
|
+
enumerable: true,
|
|
124
|
+
configurable: true,
|
|
125
|
+
writable: true
|
|
126
|
+
});
|
|
127
|
+
else obj[key] = value;
|
|
128
|
+
return obj;
|
|
129
|
+
}
|
|
130
|
+
const IMAGE_MINIMIZER_PLUGIN_NAME = '@rsbuild/plugin-image-compress/minimizer';
|
|
131
|
+
class ImageMinimizerPlugin {
|
|
132
|
+
async optimize(compiler, compilation, assets) {
|
|
133
|
+
const cache = compilation.getCache(IMAGE_MINIMIZER_PLUGIN_NAME);
|
|
134
|
+
const { RawSource } = compiler.webpack.sources;
|
|
135
|
+
const { matchObject } = compiler.webpack.ModuleFilenameHelpers;
|
|
136
|
+
const buildError = (error, file, context)=>{
|
|
137
|
+
const cause = error instanceof Error ? error : new Error();
|
|
138
|
+
const message = file && context ? `"${file}" in "${context}" from Image Minimizer:\n${cause.message}` : cause.message;
|
|
139
|
+
const ret = new compiler.webpack.WebpackError(message);
|
|
140
|
+
if (error instanceof Error) ret.error = error;
|
|
141
|
+
return ret;
|
|
142
|
+
};
|
|
143
|
+
const codec = shared_codecs[this.options.use];
|
|
144
|
+
if (!codec) compilation.errors.push(buildError(new Error(`Codec ${this.options.use} is not supported`)));
|
|
145
|
+
const opts = {
|
|
146
|
+
...codec.defaultOptions,
|
|
147
|
+
...this.options
|
|
148
|
+
};
|
|
149
|
+
const handleAsset = async (name)=>{
|
|
150
|
+
var _compilation_getAsset;
|
|
151
|
+
const info = null === (_compilation_getAsset = compilation.getAsset(name)) || void 0 === _compilation_getAsset ? void 0 : _compilation_getAsset.info;
|
|
152
|
+
const fileName = name.split('?')[0];
|
|
153
|
+
// 1. Skip double minimize assets from child compilation
|
|
154
|
+
// 2. Test file by options (e.g. test, include, exclude)
|
|
155
|
+
if ((null == info ? void 0 : info.minimized) || !matchObject(opts, fileName)) return;
|
|
156
|
+
const asset = compilation.getAsset(name);
|
|
157
|
+
if (!asset) return;
|
|
158
|
+
const { source: inputSource } = asset;
|
|
159
|
+
const eTag = cache.getLazyHashedEtag(inputSource);
|
|
160
|
+
const cacheItem = cache.getItemCache(name, eTag);
|
|
161
|
+
let result = await cacheItem.getPromise();
|
|
162
|
+
try {
|
|
163
|
+
if (!result) {
|
|
164
|
+
const input = inputSource.source();
|
|
165
|
+
const buf = await codec.handler(external_node_buffer_namespaceObject.Buffer.from(input), opts);
|
|
166
|
+
result = {
|
|
167
|
+
source: new RawSource(buf)
|
|
168
|
+
};
|
|
169
|
+
await cacheItem.storePromise(result);
|
|
170
|
+
}
|
|
171
|
+
compilation.updateAsset(name, result.source, {
|
|
172
|
+
minimized: true
|
|
173
|
+
});
|
|
174
|
+
} catch (error) {
|
|
175
|
+
compilation.errors.push(buildError(error, name, compiler.context));
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const promises = Object.keys(assets).map((name)=>handleAsset(name));
|
|
179
|
+
await Promise.all(promises);
|
|
123
180
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
await Promise.all(promises);
|
|
153
|
-
}
|
|
154
|
-
apply(compiler) {
|
|
155
|
-
const handleCompilation = (compilation) => {
|
|
156
|
-
compilation.hooks.processAssets.tapPromise(
|
|
157
|
-
{
|
|
158
|
-
name: this.name,
|
|
159
|
-
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
|
|
160
|
-
// @ts-expect-error unsupported by Rspack
|
|
161
|
-
additionalAssets: true
|
|
162
|
-
},
|
|
163
|
-
(assets) => this.optimize(compiler, compilation, assets)
|
|
164
|
-
);
|
|
165
|
-
compilation.hooks.statsPrinter.tap(this.name, (stats) => {
|
|
166
|
-
stats.hooks.print.for("asset.info.minimized").tap(
|
|
167
|
-
"@rsbuild/plugin-image-compress",
|
|
168
|
-
(minimized, { green, formatFlag }) => minimized && green && formatFlag ? green(formatFlag("minimized")) : ""
|
|
169
|
-
);
|
|
170
|
-
});
|
|
181
|
+
apply(compiler) {
|
|
182
|
+
const handleCompilation = (compilation)=>{
|
|
183
|
+
compilation.hooks.processAssets.tapPromise({
|
|
184
|
+
name: this.name,
|
|
185
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
|
|
186
|
+
// @ts-expect-error unsupported by Rspack
|
|
187
|
+
additionalAssets: true
|
|
188
|
+
}, (assets)=>this.optimize(compiler, compilation, assets));
|
|
189
|
+
compilation.hooks.statsPrinter.tap(this.name, (stats)=>{
|
|
190
|
+
stats.hooks.print.for('asset.info.minimized').tap('@rsbuild/plugin-image-compress', (minimized, { green, formatFlag })=>minimized && green && formatFlag ? green(formatFlag('minimized')) : '');
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
compiler.hooks.compilation.tap(this.name, handleCompilation);
|
|
194
|
+
}
|
|
195
|
+
constructor(options){
|
|
196
|
+
_define_property(this, "name", IMAGE_MINIMIZER_PLUGIN_NAME);
|
|
197
|
+
_define_property(this, "options", void 0);
|
|
198
|
+
this.options = options;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const withDefaultOptions = (opt)=>{
|
|
202
|
+
const options = 'string' == typeof opt ? {
|
|
203
|
+
use: opt
|
|
204
|
+
} : opt;
|
|
205
|
+
const { defaultOptions } = shared_codecs[options.use];
|
|
206
|
+
const ret = {
|
|
207
|
+
...defaultOptions,
|
|
208
|
+
...options
|
|
171
209
|
};
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// src/shared/utils.ts
|
|
177
|
-
var import_node_assert = __toESM(require("assert"), 1);
|
|
178
|
-
var withDefaultOptions = (opt) => {
|
|
179
|
-
const options = typeof opt === "string" ? { use: opt } : opt;
|
|
180
|
-
const { defaultOptions } = codecs_default[options.use];
|
|
181
|
-
const ret = { ...defaultOptions, ...options };
|
|
182
|
-
(0, import_node_assert.default)("test" in ret);
|
|
183
|
-
return ret;
|
|
210
|
+
external_node_assert_default()('test' in ret);
|
|
211
|
+
return ret;
|
|
184
212
|
};
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
213
|
+
const DEFAULT_OPTIONS = [
|
|
214
|
+
'jpeg',
|
|
215
|
+
'png',
|
|
216
|
+
'ico'
|
|
217
|
+
];
|
|
218
|
+
const castOptions = (args)=>{
|
|
219
|
+
const head = args[0];
|
|
220
|
+
// expect [['png', { use: 'jpeg' }]]
|
|
221
|
+
if (Array.isArray(head)) return head;
|
|
222
|
+
// expect ['png', { use: 'jpeg' }]
|
|
223
|
+
const ret = [];
|
|
224
|
+
for (const arg of args){
|
|
225
|
+
external_node_assert_default()(!Array.isArray(arg));
|
|
226
|
+
ret.push(arg);
|
|
227
|
+
}
|
|
228
|
+
return ret;
|
|
199
229
|
};
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
230
|
+
const normalizeOptions = (options)=>{
|
|
231
|
+
const opts = options.length ? options : DEFAULT_OPTIONS;
|
|
232
|
+
const normalized = opts.map((opt)=>withDefaultOptions(opt));
|
|
233
|
+
return normalized;
|
|
204
234
|
};
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
235
|
+
const PLUGIN_IMAGE_COMPRESS_NAME = 'rsbuild:image-compress';
|
|
236
|
+
/** Options enable by default: {@link DEFAULT_OPTIONS} */ const pluginImageCompress = (...args)=>({
|
|
237
|
+
name: PLUGIN_IMAGE_COMPRESS_NAME,
|
|
238
|
+
setup (api) {
|
|
239
|
+
const opts = normalizeOptions(castOptions(args));
|
|
240
|
+
api.modifyBundlerChain((chain, { isDev })=>{
|
|
241
|
+
if (isDev) return;
|
|
242
|
+
chain.optimization.minimize(true);
|
|
243
|
+
for (const opt of opts)chain.optimization.minimizer(`image-compress-${opt.use}`).use(ImageMinimizerPlugin, [
|
|
244
|
+
opt
|
|
245
|
+
]);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
218
248
|
});
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
DEFAULT_OPTIONS,
|
|
224
|
-
PLUGIN_IMAGE_COMPRESS_NAME,
|
|
225
|
-
pluginImageCompress
|
|
249
|
+
var __webpack_export_target__ = exports;
|
|
250
|
+
for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
251
|
+
if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', {
|
|
252
|
+
value: true
|
|
226
253
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,38 +1,11 @@
|
|
|
1
|
-
import { RsbuildPlugin } from '@rsbuild/core';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface CodecBaseOptions {
|
|
7
|
-
jpeg: JpegCompressOptions;
|
|
8
|
-
png: PngQuantOptions;
|
|
9
|
-
pngLossless: PNGLosslessOptions;
|
|
10
|
-
ico: Record<string, unknown>;
|
|
11
|
-
svg: Config;
|
|
12
|
-
}
|
|
13
|
-
interface BaseCompressOptions<T extends Codecs> {
|
|
14
|
-
use: T;
|
|
15
|
-
test?: OneOrMany<RegExp>;
|
|
16
|
-
include?: OneOrMany<RegExp>;
|
|
17
|
-
exclude?: OneOrMany<RegExp>;
|
|
18
|
-
}
|
|
19
|
-
type FinalOptionCollection = {
|
|
20
|
-
[K in Codecs]: BaseCompressOptions<K> & CodecBaseOptions[K];
|
|
21
|
-
};
|
|
22
|
-
type Codecs = keyof CodecBaseOptions;
|
|
23
|
-
type OptionCollection = {
|
|
24
|
-
[K in Codecs]: K | FinalOptionCollection[K];
|
|
25
|
-
};
|
|
26
|
-
type Options = OptionCollection[Codecs];
|
|
27
|
-
|
|
28
|
-
type PluginImageCompressOptions = Options[];
|
|
29
|
-
declare const DEFAULT_OPTIONS: Codecs[];
|
|
30
|
-
interface IPluginImageCompress {
|
|
1
|
+
import type { RsbuildPlugin } from '@rsbuild/core';
|
|
2
|
+
import type { Codecs, Options } from './types.js';
|
|
3
|
+
export type PluginImageCompressOptions = Options[];
|
|
4
|
+
export declare const DEFAULT_OPTIONS: Codecs[];
|
|
5
|
+
export interface IPluginImageCompress {
|
|
31
6
|
(...options: Options[]): RsbuildPlugin;
|
|
32
7
|
(options: Options[]): RsbuildPlugin;
|
|
33
8
|
}
|
|
34
|
-
declare const PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
|
|
9
|
+
export declare const PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
|
|
35
10
|
/** Options enable by default: {@link DEFAULT_OPTIONS} */
|
|
36
|
-
declare const pluginImageCompress: IPluginImageCompress;
|
|
37
|
-
|
|
38
|
-
export { DEFAULT_OPTIONS, type IPluginImageCompress, PLUGIN_IMAGE_COMPRESS_NAME, type PluginImageCompressOptions, pluginImageCompress };
|
|
11
|
+
export declare const pluginImageCompress: IPluginImageCompress;
|
package/dist/index.js
CHANGED
|
@@ -1,194 +1,193 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
losslessCompressPng,
|
|
13
|
-
pngQuantize
|
|
14
|
-
} from "@napi-rs/image";
|
|
15
|
-
import svgo from "svgo";
|
|
16
|
-
var jpegCodec = {
|
|
17
|
-
handler(buf, options) {
|
|
18
|
-
return compressJpeg(buf, options);
|
|
19
|
-
},
|
|
20
|
-
defaultOptions: {
|
|
21
|
-
test: /\.(?:jpg|jpeg)$/
|
|
22
|
-
}
|
|
1
|
+
import * as __WEBPACK_EXTERNAL_MODULE_node_assert__ from "node:assert";
|
|
2
|
+
import * as __WEBPACK_EXTERNAL_MODULE_node_buffer__ from "node:buffer";
|
|
3
|
+
import * as __WEBPACK_EXTERNAL_MODULE__napi_rs_image__ from "@napi-rs/image";
|
|
4
|
+
import * as __WEBPACK_EXTERNAL_MODULE_svgo__ from "svgo";
|
|
5
|
+
const jpegCodec = {
|
|
6
|
+
handler (buf, options) {
|
|
7
|
+
return (0, __WEBPACK_EXTERNAL_MODULE__napi_rs_image__.compressJpeg)(buf, options);
|
|
8
|
+
},
|
|
9
|
+
defaultOptions: {
|
|
10
|
+
test: /\.(?:jpg|jpeg)$/
|
|
11
|
+
}
|
|
23
12
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
const pngCodec = {
|
|
14
|
+
handler (buf, options) {
|
|
15
|
+
return (0, __WEBPACK_EXTERNAL_MODULE__napi_rs_image__.pngQuantize)(buf, options);
|
|
16
|
+
},
|
|
17
|
+
defaultOptions: {
|
|
18
|
+
test: /\.png$/
|
|
19
|
+
}
|
|
31
20
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
const pngLosslessCodec = {
|
|
22
|
+
handler (buf, options) {
|
|
23
|
+
return (0, __WEBPACK_EXTERNAL_MODULE__napi_rs_image__.losslessCompressPng)(buf, options);
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: {
|
|
26
|
+
test: /\.png$/
|
|
27
|
+
}
|
|
39
28
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
29
|
+
const icoCodec = {
|
|
30
|
+
handler (buf) {
|
|
31
|
+
return new __WEBPACK_EXTERNAL_MODULE__napi_rs_image__.Transformer(buf).ico();
|
|
32
|
+
},
|
|
33
|
+
defaultOptions: {
|
|
34
|
+
test: /\.(?:ico|icon)$/
|
|
35
|
+
}
|
|
47
36
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
37
|
+
const avifCodec = {
|
|
38
|
+
handler (buf, options) {
|
|
39
|
+
return new __WEBPACK_EXTERNAL_MODULE__napi_rs_image__.Transformer(buf).avif(options);
|
|
40
|
+
},
|
|
41
|
+
defaultOptions: {
|
|
42
|
+
test: /\.avif$/
|
|
43
|
+
}
|
|
56
44
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
const svgCodec = {
|
|
46
|
+
async handler (buf, options) {
|
|
47
|
+
const result = __WEBPACK_EXTERNAL_MODULE_svgo__["default"].optimize(buf.toString(), options);
|
|
48
|
+
return __WEBPACK_EXTERNAL_MODULE_node_buffer__.Buffer.from(result.data);
|
|
49
|
+
},
|
|
50
|
+
defaultOptions: {
|
|
51
|
+
test: /\.svg$/
|
|
52
|
+
}
|
|
63
53
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
buildError
|
|
91
|
-
|
|
54
|
+
// biome-ignore lint/suspicious/noExplicitAny:allow any
|
|
55
|
+
const codecs = {
|
|
56
|
+
jpeg: jpegCodec,
|
|
57
|
+
png: pngCodec,
|
|
58
|
+
pngLossless: pngLosslessCodec,
|
|
59
|
+
ico: icoCodec,
|
|
60
|
+
svg: svgCodec,
|
|
61
|
+
avif: avifCodec
|
|
62
|
+
};
|
|
63
|
+
/* ESM default export */ const shared_codecs = codecs;
|
|
64
|
+
function _define_property(obj, key, value) {
|
|
65
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
66
|
+
value: value,
|
|
67
|
+
enumerable: true,
|
|
68
|
+
configurable: true,
|
|
69
|
+
writable: true
|
|
70
|
+
});
|
|
71
|
+
else obj[key] = value;
|
|
72
|
+
return obj;
|
|
73
|
+
}
|
|
74
|
+
const IMAGE_MINIMIZER_PLUGIN_NAME = '@rsbuild/plugin-image-compress/minimizer';
|
|
75
|
+
class ImageMinimizerPlugin {
|
|
76
|
+
async optimize(compiler, compilation, assets) {
|
|
77
|
+
const cache = compilation.getCache(IMAGE_MINIMIZER_PLUGIN_NAME);
|
|
78
|
+
const { RawSource } = compiler.webpack.sources;
|
|
79
|
+
const { matchObject } = compiler.webpack.ModuleFilenameHelpers;
|
|
80
|
+
const buildError = (error, file, context)=>{
|
|
81
|
+
const cause = error instanceof Error ? error : new Error();
|
|
82
|
+
const message = file && context ? `"${file}" in "${context}" from Image Minimizer:\n${cause.message}` : cause.message;
|
|
83
|
+
const ret = new compiler.webpack.WebpackError(message);
|
|
84
|
+
if (error instanceof Error) ret.error = error;
|
|
85
|
+
return ret;
|
|
86
|
+
};
|
|
87
|
+
const codec = shared_codecs[this.options.use];
|
|
88
|
+
if (!codec) compilation.errors.push(buildError(new Error(`Codec ${this.options.use} is not supported`)));
|
|
89
|
+
const opts = {
|
|
90
|
+
...codec.defaultOptions,
|
|
91
|
+
...this.options
|
|
92
|
+
};
|
|
93
|
+
const handleAsset = async (name)=>{
|
|
94
|
+
var _compilation_getAsset;
|
|
95
|
+
const info = null === (_compilation_getAsset = compilation.getAsset(name)) || void 0 === _compilation_getAsset ? void 0 : _compilation_getAsset.info;
|
|
96
|
+
const fileName = name.split('?')[0];
|
|
97
|
+
// 1. Skip double minimize assets from child compilation
|
|
98
|
+
// 2. Test file by options (e.g. test, include, exclude)
|
|
99
|
+
if ((null == info ? void 0 : info.minimized) || !matchObject(opts, fileName)) return;
|
|
100
|
+
const asset = compilation.getAsset(name);
|
|
101
|
+
if (!asset) return;
|
|
102
|
+
const { source: inputSource } = asset;
|
|
103
|
+
const eTag = cache.getLazyHashedEtag(inputSource);
|
|
104
|
+
const cacheItem = cache.getItemCache(name, eTag);
|
|
105
|
+
let result = await cacheItem.getPromise();
|
|
106
|
+
try {
|
|
107
|
+
if (!result) {
|
|
108
|
+
const input = inputSource.source();
|
|
109
|
+
const buf = await codec.handler(__WEBPACK_EXTERNAL_MODULE_node_buffer__.Buffer.from(input), opts);
|
|
110
|
+
result = {
|
|
111
|
+
source: new RawSource(buf)
|
|
112
|
+
};
|
|
113
|
+
await cacheItem.storePromise(result);
|
|
114
|
+
}
|
|
115
|
+
compilation.updateAsset(name, result.source, {
|
|
116
|
+
minimized: true
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
compilation.errors.push(buildError(error, name, compiler.context));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const promises = Object.keys(assets).map((name)=>handleAsset(name));
|
|
123
|
+
await Promise.all(promises);
|
|
92
124
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
await Promise.all(promises);
|
|
122
|
-
}
|
|
123
|
-
apply(compiler) {
|
|
124
|
-
const handleCompilation = (compilation) => {
|
|
125
|
-
compilation.hooks.processAssets.tapPromise(
|
|
126
|
-
{
|
|
127
|
-
name: this.name,
|
|
128
|
-
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
|
|
129
|
-
// @ts-expect-error unsupported by Rspack
|
|
130
|
-
additionalAssets: true
|
|
131
|
-
},
|
|
132
|
-
(assets) => this.optimize(compiler, compilation, assets)
|
|
133
|
-
);
|
|
134
|
-
compilation.hooks.statsPrinter.tap(this.name, (stats) => {
|
|
135
|
-
stats.hooks.print.for("asset.info.minimized").tap(
|
|
136
|
-
"@rsbuild/plugin-image-compress",
|
|
137
|
-
(minimized, { green, formatFlag }) => minimized && green && formatFlag ? green(formatFlag("minimized")) : ""
|
|
138
|
-
);
|
|
139
|
-
});
|
|
125
|
+
apply(compiler) {
|
|
126
|
+
const handleCompilation = (compilation)=>{
|
|
127
|
+
compilation.hooks.processAssets.tapPromise({
|
|
128
|
+
name: this.name,
|
|
129
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
|
|
130
|
+
// @ts-expect-error unsupported by Rspack
|
|
131
|
+
additionalAssets: true
|
|
132
|
+
}, (assets)=>this.optimize(compiler, compilation, assets));
|
|
133
|
+
compilation.hooks.statsPrinter.tap(this.name, (stats)=>{
|
|
134
|
+
stats.hooks.print.for('asset.info.minimized').tap('@rsbuild/plugin-image-compress', (minimized, { green, formatFlag })=>minimized && green && formatFlag ? green(formatFlag('minimized')) : '');
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
compiler.hooks.compilation.tap(this.name, handleCompilation);
|
|
138
|
+
}
|
|
139
|
+
constructor(options){
|
|
140
|
+
_define_property(this, "name", IMAGE_MINIMIZER_PLUGIN_NAME);
|
|
141
|
+
_define_property(this, "options", void 0);
|
|
142
|
+
this.options = options;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const withDefaultOptions = (opt)=>{
|
|
146
|
+
const options = 'string' == typeof opt ? {
|
|
147
|
+
use: opt
|
|
148
|
+
} : opt;
|
|
149
|
+
const { defaultOptions } = shared_codecs[options.use];
|
|
150
|
+
const ret = {
|
|
151
|
+
...defaultOptions,
|
|
152
|
+
...options
|
|
140
153
|
};
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
(0, __WEBPACK_EXTERNAL_MODULE_node_assert__["default"])('test' in ret);
|
|
155
|
+
return ret;
|
|
143
156
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return head;
|
|
161
|
-
}
|
|
162
|
-
const ret = [];
|
|
163
|
-
for (const arg of args) {
|
|
164
|
-
assert2(!Array.isArray(arg));
|
|
165
|
-
ret.push(arg);
|
|
166
|
-
}
|
|
167
|
-
return ret;
|
|
157
|
+
const DEFAULT_OPTIONS = [
|
|
158
|
+
'jpeg',
|
|
159
|
+
'png',
|
|
160
|
+
'ico'
|
|
161
|
+
];
|
|
162
|
+
const castOptions = (args)=>{
|
|
163
|
+
const head = args[0];
|
|
164
|
+
// expect [['png', { use: 'jpeg' }]]
|
|
165
|
+
if (Array.isArray(head)) return head;
|
|
166
|
+
// expect ['png', { use: 'jpeg' }]
|
|
167
|
+
const ret = [];
|
|
168
|
+
for (const arg of args){
|
|
169
|
+
(0, __WEBPACK_EXTERNAL_MODULE_node_assert__["default"])(!Array.isArray(arg));
|
|
170
|
+
ret.push(arg);
|
|
171
|
+
}
|
|
172
|
+
return ret;
|
|
168
173
|
};
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
const normalizeOptions = (options)=>{
|
|
175
|
+
const opts = options.length ? options : DEFAULT_OPTIONS;
|
|
176
|
+
const normalized = opts.map((opt)=>withDefaultOptions(opt));
|
|
177
|
+
return normalized;
|
|
173
178
|
};
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
179
|
+
const PLUGIN_IMAGE_COMPRESS_NAME = 'rsbuild:image-compress';
|
|
180
|
+
/** Options enable by default: {@link DEFAULT_OPTIONS} */ const pluginImageCompress = (...args)=>({
|
|
181
|
+
name: PLUGIN_IMAGE_COMPRESS_NAME,
|
|
182
|
+
setup (api) {
|
|
183
|
+
const opts = normalizeOptions(castOptions(args));
|
|
184
|
+
api.modifyBundlerChain((chain, { isDev })=>{
|
|
185
|
+
if (isDev) return;
|
|
186
|
+
chain.optimization.minimize(true);
|
|
187
|
+
for (const opt of opts)chain.optimization.minimizer(`image-compress-${opt.use}`).use(ImageMinimizerPlugin, [
|
|
188
|
+
opt
|
|
189
|
+
]);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
187
192
|
});
|
|
188
|
-
|
|
189
|
-
});
|
|
190
|
-
export {
|
|
191
|
-
DEFAULT_OPTIONS,
|
|
192
|
-
PLUGIN_IMAGE_COMPRESS_NAME,
|
|
193
|
-
pluginImageCompress
|
|
194
|
-
};
|
|
193
|
+
export { DEFAULT_OPTIONS, PLUGIN_IMAGE_COMPRESS_NAME, pluginImageCompress };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Rspack } from '@rsbuild/core';
|
|
2
|
+
import type { FinalOptions } from './types.js';
|
|
3
|
+
export declare const IMAGE_MINIMIZER_PLUGIN_NAME: "@rsbuild/plugin-image-compress/minimizer";
|
|
4
|
+
export interface MinimizedResult {
|
|
5
|
+
source: Rspack.sources.RawSource;
|
|
6
|
+
}
|
|
7
|
+
export declare class ImageMinimizerPlugin {
|
|
8
|
+
name: string;
|
|
9
|
+
options: FinalOptions;
|
|
10
|
+
constructor(options: FinalOptions);
|
|
11
|
+
optimize(compiler: Rspack.Compiler, compilation: Rspack.Compilation, assets: Record<string, Rspack.sources.Source>): Promise<void>;
|
|
12
|
+
apply(compiler: Rspack.Compiler): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Codec, Codecs } from '../types.js';
|
|
2
|
+
export declare const jpegCodec: Codec<'jpeg'>;
|
|
3
|
+
export declare const pngCodec: Codec<'png'>;
|
|
4
|
+
export declare const pngLosslessCodec: Codec<'pngLossless'>;
|
|
5
|
+
export declare const icoCodec: Codec<'ico'>;
|
|
6
|
+
export declare const avifCodec: Codec<'avif'>;
|
|
7
|
+
export declare const svgCodec: Codec<'svg'>;
|
|
8
|
+
declare const codecs: Record<Codecs, Codec<any>>;
|
|
9
|
+
export default codecs;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Buffer } from 'node:buffer';
|
|
2
|
+
import type { AvifConfig, JpegCompressOptions, PNGLosslessOptions, PngQuantOptions } from '@napi-rs/image';
|
|
3
|
+
import type { Config as SvgoConfig } from 'svgo';
|
|
4
|
+
export type OneOrMany<T> = T | T[];
|
|
5
|
+
export interface WebpTransformOptions {
|
|
6
|
+
quality?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CodecBaseOptions {
|
|
9
|
+
jpeg: JpegCompressOptions;
|
|
10
|
+
png: PngQuantOptions;
|
|
11
|
+
pngLossless: PNGLosslessOptions;
|
|
12
|
+
ico: Record<string, unknown>;
|
|
13
|
+
svg: SvgoConfig;
|
|
14
|
+
avif: AvifConfig;
|
|
15
|
+
}
|
|
16
|
+
export interface BaseCompressOptions<T extends Codecs> {
|
|
17
|
+
use: T;
|
|
18
|
+
test?: OneOrMany<RegExp>;
|
|
19
|
+
include?: OneOrMany<RegExp>;
|
|
20
|
+
exclude?: OneOrMany<RegExp>;
|
|
21
|
+
}
|
|
22
|
+
export type FinalOptionCollection = {
|
|
23
|
+
[K in Codecs]: BaseCompressOptions<K> & CodecBaseOptions[K];
|
|
24
|
+
};
|
|
25
|
+
export type FinalOptions = FinalOptionCollection[Codecs];
|
|
26
|
+
export interface Codec<T extends Codecs> {
|
|
27
|
+
handler: (buf: Buffer, options: CodecBaseOptions[T]) => Promise<Buffer>;
|
|
28
|
+
defaultOptions: Omit<FinalOptionCollection[T], 'use'>;
|
|
29
|
+
}
|
|
30
|
+
export type Codecs = keyof CodecBaseOptions;
|
|
31
|
+
export type OptionCollection = {
|
|
32
|
+
[K in Codecs]: K | FinalOptionCollection[K];
|
|
33
|
+
};
|
|
34
|
+
export type Options = OptionCollection[Codecs];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsbuild/plugin-image-compress",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"repository": "https://github.com/rspack-contrib/rsbuild-plugin-image-compress",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,9 +14,16 @@
|
|
|
14
14
|
"main": "./dist/index.js",
|
|
15
15
|
"module": "./dist/index.mjs",
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
|
-
"files": [
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
"files": ["dist"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "rslib build",
|
|
20
|
+
"dev": "rslib build --watch",
|
|
21
|
+
"lint": "biome check .",
|
|
22
|
+
"lint:write": "biome check . --write",
|
|
23
|
+
"prepare": "simple-git-hooks && npm run build",
|
|
24
|
+
"test": "playwright test",
|
|
25
|
+
"bump": "npx bumpp"
|
|
26
|
+
},
|
|
20
27
|
"simple-git-hooks": {
|
|
21
28
|
"pre-commit": "npx nano-staged"
|
|
22
29
|
},
|
|
@@ -30,33 +37,27 @@
|
|
|
30
37
|
"svgo": "^3.3.2"
|
|
31
38
|
},
|
|
32
39
|
"devDependencies": {
|
|
33
|
-
"@biomejs/biome": "^1.
|
|
34
|
-
"@playwright/test": "^1.
|
|
35
|
-
"@rsbuild/core": "1.
|
|
36
|
-
"@
|
|
40
|
+
"@biomejs/biome": "^1.9.4",
|
|
41
|
+
"@playwright/test": "^1.49.0",
|
|
42
|
+
"@rsbuild/core": "1.1.6",
|
|
43
|
+
"@rslib/core": "^0.1.1",
|
|
44
|
+
"@types/node": "^22.10.1",
|
|
37
45
|
"nano-staged": "^0.8.0",
|
|
38
|
-
"playwright": "^1.
|
|
46
|
+
"playwright": "^1.49.0",
|
|
39
47
|
"simple-git-hooks": "^2.11.1",
|
|
40
|
-
"
|
|
41
|
-
"typescript": "^5.5.4"
|
|
48
|
+
"typescript": "^5.7.2"
|
|
42
49
|
},
|
|
43
50
|
"peerDependencies": {
|
|
44
|
-
"@rsbuild/core": "1.x
|
|
51
|
+
"@rsbuild/core": "1.x"
|
|
45
52
|
},
|
|
46
53
|
"peerDependenciesMeta": {
|
|
47
54
|
"@rsbuild/core": {
|
|
48
55
|
"optional": true
|
|
49
56
|
}
|
|
50
57
|
},
|
|
58
|
+
"packageManager": "pnpm@9.14.4",
|
|
51
59
|
"publishConfig": {
|
|
52
60
|
"access": "public",
|
|
53
61
|
"registry": "https://registry.npmjs.org/"
|
|
54
|
-
},
|
|
55
|
-
"scripts": {
|
|
56
|
-
"build": "tsup",
|
|
57
|
-
"dev": "tsup --watch",
|
|
58
|
-
"lint": "biome check .",
|
|
59
|
-
"lint:write": "biome check . --write",
|
|
60
|
-
"test": "playwright test"
|
|
61
62
|
}
|
|
62
|
-
}
|
|
63
|
+
}
|
package/dist/index.d.cts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { RsbuildPlugin } from '@rsbuild/core';
|
|
2
|
-
import { JpegCompressOptions, PngQuantOptions, PNGLosslessOptions } from '@napi-rs/image';
|
|
3
|
-
import { Config } from 'svgo';
|
|
4
|
-
|
|
5
|
-
type OneOrMany<T> = T | T[];
|
|
6
|
-
interface CodecBaseOptions {
|
|
7
|
-
jpeg: JpegCompressOptions;
|
|
8
|
-
png: PngQuantOptions;
|
|
9
|
-
pngLossless: PNGLosslessOptions;
|
|
10
|
-
ico: Record<string, unknown>;
|
|
11
|
-
svg: Config;
|
|
12
|
-
}
|
|
13
|
-
interface BaseCompressOptions<T extends Codecs> {
|
|
14
|
-
use: T;
|
|
15
|
-
test?: OneOrMany<RegExp>;
|
|
16
|
-
include?: OneOrMany<RegExp>;
|
|
17
|
-
exclude?: OneOrMany<RegExp>;
|
|
18
|
-
}
|
|
19
|
-
type FinalOptionCollection = {
|
|
20
|
-
[K in Codecs]: BaseCompressOptions<K> & CodecBaseOptions[K];
|
|
21
|
-
};
|
|
22
|
-
type Codecs = keyof CodecBaseOptions;
|
|
23
|
-
type OptionCollection = {
|
|
24
|
-
[K in Codecs]: K | FinalOptionCollection[K];
|
|
25
|
-
};
|
|
26
|
-
type Options = OptionCollection[Codecs];
|
|
27
|
-
|
|
28
|
-
type PluginImageCompressOptions = Options[];
|
|
29
|
-
declare const DEFAULT_OPTIONS: Codecs[];
|
|
30
|
-
interface IPluginImageCompress {
|
|
31
|
-
(...options: Options[]): RsbuildPlugin;
|
|
32
|
-
(options: Options[]): RsbuildPlugin;
|
|
33
|
-
}
|
|
34
|
-
declare const PLUGIN_IMAGE_COMPRESS_NAME = "rsbuild:image-compress";
|
|
35
|
-
/** Options enable by default: {@link DEFAULT_OPTIONS} */
|
|
36
|
-
declare const pluginImageCompress: IPluginImageCompress;
|
|
37
|
-
|
|
38
|
-
export { DEFAULT_OPTIONS, type IPluginImageCompress, PLUGIN_IMAGE_COMPRESS_NAME, type PluginImageCompressOptions, pluginImageCompress };
|