diff --git a/CHANGELOG.md b/CHANGELOG.md index fbea34b..4d63dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +* Unreleased | 14/03.2022 + - added `skipUnchanged` option + * V3.3.6 | 19/07.2021 - added support for Webpack 5 (https://github.com/iampava/imagemin-webp-webpack-plugin/issues/38) diff --git a/README.md b/README.md index 5b67442..bd9d6f6 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,13 @@ By default the webpack build will fail if any of the images that match your RegE This option tells the plugin to not crash the build and keep going :) +#### skipUnchanged + +Type: `boolean`
+Default: `false` + +If enabled, unchanged images are skipped during fast-refresh, and only new images are converted. + ## Compatibility & known issues Recently we updated this plugin to make it compatible with **webpack 5**. Originally it was built for **webpack 4** and earlier versions, so I expect it would be compatible no matter the project :) diff --git a/plugin.js b/plugin.js index 58d822a..e060fdb 100644 --- a/plugin.js +++ b/plugin.js @@ -1,6 +1,7 @@ const imagemin = require('imagemin'); const webp = require('imagemin-webp'); const gif2webp = require('imagemin-gif2webp'); +const crypto = require('crypto'); const GREEN = '\x1b[32m%s\x1b[0m'; const RED = '\x1b[31m%s\x1b[0m'; @@ -18,19 +19,24 @@ class ImageminWebpWebpackPlugin { overrideExtension = true, detailedLogs = false, strict = true, - silent = false + silent = false, + skipUnchanged = false, } = {}) { this.config = config; this.detailedLogs = detailedLogs; this.strict = strict; this.overrideExtension = overrideExtension; this.silent = silent; + this.skipUnchanged = skipUnchanged; + this.sourceVersions = {}; + this.savedKBs = {}; } apply(compiler) { const onEmit = (compilation, cb) => { let assetNames = Object.keys(compilation.assets); let nrOfImagesFailed = 0; + let nrOfImagesSkipped = 0; if (this.silent && this.detailedLogs) { compilation.warnings.push(new Error(`ImageminWebpWebpackPlugin: both the 'silent' and 'detailedLogs' options are true. Overriding 'detailedLogs' and disabling all console output.`)); @@ -51,6 +57,20 @@ class ImageminWebpWebpackPlugin { let currentAsset = compilation.assets[name]; + let hash = undefined; + if (this.skipUnchanged) { + hash = crypto.createHash('sha256').update(currentAsset.source()).digest('hex'); + + if (this.sourceVersions[outputName] === hash) { + nrOfImagesSkipped++; + if (this.detailedLogs && !this.silent) { + console.log(GREEN, `Unchanged and skipped: '${name}'`); + } + return Promise.resolve(this.savedKBs[hash] ?? 0); + } + this.sourceVersions[outputName] = hash; + } + return imagemin .buffer(currentAsset.source(), { plugins: [ @@ -66,6 +86,11 @@ class ImageminWebpWebpackPlugin { } emitAsset(outputName, buffer, compilation); + + if (this.skipUnchanged && hash !== undefined) { + this.savedKBs[hash] = savedKB; + } + return savedKB; }) .catch(err => { @@ -95,6 +120,9 @@ class ImageminWebpWebpackPlugin { console.log(GREEN, `imagemin-webp-webpack-plugin: ${Math.floor(totalKBSaved / 100) / 10} MB saved`); } + if (nrOfImagesSkipped > 0) { + console.log(GREEN, `imagemin-webp-webpack-plugin: ${nrOfImagesSkipped} images were unchanged and skipped`); + } if (nrOfImagesFailed > 0) { console.log(RED, `imagemin-webp-webpack-plugin: ${nrOfImagesFailed} images failed to convert to webp`); }