rsbuild-plugin-sharp-image-optimizer
Version:
A Rsbuild plugin for image optimization using Sharp
132 lines (131 loc) • 6.92 kB
JavaScript
import * as __WEBPACK_EXTERNAL_MODULE_sharp__ from "sharp";
import * as __WEBPACK_EXTERNAL_MODULE_path__ from "path";
class SharpImageOptimizerPlugin {
async optimize(compiler, compilation, assets) {
let { RawSource } = compiler.webpack.sources, processedAssets = new Map();
for (let [name, asset] of Object.entries(assets)){
if (!!this.options.test.test(name)) try {
let outputBuffer;
let inputBuffer = asset.source(), ext = __WEBPACK_EXTERNAL_MODULE_path__.default.extname(name).toLowerCase(), originalFormat = ext.slice(1), sharpInstance = (0, __WEBPACK_EXTERNAL_MODULE_sharp__.default)(inputBuffer), newName = name;
if (this.options.format && this.options.format !== originalFormat && !('jpg' === this.options.format && 'jpeg' === originalFormat) && !('jpeg' === this.options.format && 'jpg' === originalFormat)) {
var _compilation_getAsset;
switch(newName = name.replace(ext, `.${this.options.format}`), this.options.format){
case 'webp':
outputBuffer = await sharpInstance.webp({
quality: this.options.quality,
effort: this.options.effort
}).toBuffer();
break;
case 'avif':
outputBuffer = await sharpInstance.avif({
quality: this.options.quality,
effort: this.options.effort
}).toBuffer();
break;
case 'png':
outputBuffer = await sharpInstance.png({
quality: this.options.quality,
effort: this.options.effort
}).toBuffer();
break;
case 'jpeg':
case 'jpg':
outputBuffer = await sharpInstance.jpeg({
quality: this.options.quality
}).toBuffer();
break;
default:
throw Error(`Unsupported format: ${this.options.format}`);
}
compilation.emitAsset(newName, new RawSource(outputBuffer), {
...null === (_compilation_getAsset = compilation.getAsset(name)) || void 0 === _compilation_getAsset ? void 0 : _compilation_getAsset.info,
sourceFilename: name
}), processedAssets.set(name, newName);
} else {
switch(originalFormat){
case 'png':
outputBuffer = await sharpInstance.png({
quality: this.options.quality,
effort: this.options.effort
}).toBuffer();
break;
case 'jpg':
case 'jpeg':
outputBuffer = await sharpInstance.jpeg({
quality: this.options.quality
}).toBuffer();
break;
case 'webp':
outputBuffer = await sharpInstance.webp({
quality: this.options.quality,
effort: this.options.effort
}).toBuffer();
break;
default:
throw Error(`Unsupported format: ${originalFormat}`);
}
compilation.updateAsset(name, new RawSource(outputBuffer), asset.info);
}
} catch (error) {
compilation.errors.push(new compiler.webpack.WebpackError(`Failed to process image ${name}: ${error instanceof Error ? error.message : String(error)}`));
}
}
if (processedAssets.size > 0) {
for (let [name, asset] of Object.entries(assets))if (name.endsWith('.js')) {
let source = asset.source().toString(), modified = !1;
for (let [oldName, newName] of processedAssets.entries()){
let oldPath = oldName.replace(/\\/g, '/'), newPath = newName.replace(/\\/g, '/');
source.includes(oldPath) && (source = source.replace(RegExp(oldPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newPath), modified = !0);
}
modified && compilation.updateAsset(name, new RawSource(source), asset.info);
}
for (let [oldName] of processedAssets)compilation.deleteAsset(oldName);
}
}
apply(compiler) {
compiler.hooks.compilation.tap('SharpImageOptimizerPlugin', (compilation)=>{
compilation.hooks.processAssets.tapPromise({
name: 'SharpImageOptimizerPlugin',
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE
}, async (assets)=>this.optimize(compiler, compilation, assets));
});
}
constructor(options = {}){
var obj, key, value;
obj = this, value = void 0, (key = "options") in obj ? Object.defineProperty(obj, key, {
value: value,
enumerable: !0,
configurable: !0,
writable: !0
}) : obj[key] = value, this.options = {
test: /\.(png|jpe?g)$/i,
quality: 85,
effort: 6,
...options
};
}
}
let sharpImageOptimizer = (options = {})=>{
let { test = /\.(jpe?g|png|webp|avif)$/i, quality = 80, effort = 4, ...pluginOptions } = options;
return {
name: 'rsbuild-plugin-sharp-image-optimizer',
setup (api) {
api.modifyBundlerChain((chain, { isProd })=>{
if (isProd) {
var _config_output_distPath, _config_output;
let imagePath = (null === (_config_output = api.getNormalizedConfig().output) || void 0 === _config_output ? void 0 : null === (_config_output_distPath = _config_output.distPath) || void 0 === _config_output_distPath ? void 0 : _config_output_distPath.image) ?? 'static/image';
chain.plugin('sharp-image-optimizer-plugin').use(SharpImageOptimizerPlugin, [
{
test,
quality,
effort,
...pluginOptions,
imagePath
}
]);
}
});
}
};
};
export { sharpImageOptimizer };