From 9fcfdff51b6bd9af4097ef6d2442d1fd594a2edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Fri, 24 Apr 2026 10:59:59 +0200 Subject: [PATCH 01/14] Add WebP support and update dependencies - Added imagemin-webp to package.json and updated yarn.lock. - Configured WebP generation in webpack.common.js. - Updated SCSS files to support WebP images. - Removed obsolete logo SVG file. --- config/webpack.common.js | 9 +++++++++ package.json | 1 + src/img/static/logo-beapi.svg | 1 - src/scss/02-tools/_m-background-static.scss | 6 +++--- src/scss/login.scss | 3 ++- 5 files changed, 15 insertions(+), 5 deletions(-) delete mode 100644 src/img/static/logo-beapi.svg diff --git a/config/webpack.common.js b/config/webpack.common.js index 1e12ba68..acc3f6ef 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -29,6 +29,15 @@ module.exports = { ], }, }, + generator: [ + { + preset: 'webp', + implementation: ImageMinimizerPlugin.imageminGenerate, + options: { + plugins: ['imagemin-webp'], + }, + }, + ], }), new TerserPlugin({ parallel: true, diff --git a/package.json b/package.json index 3f1255d9..7adceded 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "imagemin-jpegtran": "^7.0.0", "imagemin-optipng": "^8.0.0", "imagemin-svgo": "^10.0.1", + "imagemin-webp": "^8.0.0", "mini-css-extract-plugin": "^2.10.2", "postcss": "^8.5.6", "postcss-import": "^16.1.1", diff --git a/src/img/static/logo-beapi.svg b/src/img/static/logo-beapi.svg deleted file mode 100644 index 5cf0fca4..00000000 --- a/src/img/static/logo-beapi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/scss/02-tools/_m-background-static.scss b/src/scss/02-tools/_m-background-static.scss index 0da1447e..99be8bba 100644 --- a/src/scss/02-tools/_m-background-static.scss +++ b/src/scss/02-tools/_m-background-static.scss @@ -13,15 +13,15 @@ * */ -@mixin background-static($filename, $retina: true, $position: center center, $size: auto 100%, $type: "png" ) { - background-image: url(../img/static/#{$filename}.#{$type}); +@mixin background-static($filename, $retina: true, $position: center center, $size: auto 100%, $type: "png") { + background-image: url(../img/static/#{$filename}.#{$type}?as=webp); background-repeat: no-repeat; background-position: $position; background-size: $size; @if ($retina) { @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { - background-image: url(../img/static/#{$filename}@2x.#{$type}); + background-image: url(../img/static/#{$filename}@2x.#{$type}?as=webp); } } } diff --git a/src/scss/login.scss b/src/scss/login.scss index ad1fdd5f..6a69ad0e 100644 --- a/src/scss/login.scss +++ b/src/scss/login.scss @@ -1,7 +1,7 @@ // SCSS variables to customise the login page styles // The CSS file generated in dist/ by webpack is called automatically thanks to the WP Login Page mu-plugin present in the WP Skeleton. -$login-logo-path: "../../src/img/static/logo.jpg"; +$login-logo-path: "../../src/img/static/logo.jpg?as=webp"; $login-logo-size: 312px 43px; $login-logo-height: 60px; $login-body-bg-color: #666; @@ -16,6 +16,7 @@ $login-btn-hover-bg-color: #222; html, body { + &::before, &::after { display: none; From 3e32fe9fdabeef62dba688c3e49d1e6ff50a5394 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Fri, 24 Apr 2026 16:19:10 +0200 Subject: [PATCH 02/14] fix(webpack): register WebP imagemin in plugins for dev, avoid duplicate instance Move ImageMinimizerPlugin to plugins (generator + optional prod minifier) so ?as=webp works when minimization is off. Remove it from optimization.minimizer to keep a single instance and avoid asset name conflicts. Minor shorthand for mode in dev/prod config. --- config/plugins.js | 33 +++++++++++++++++++++++++++++++++ config/webpack.common.js | 27 --------------------------- config/webpack.dev.js | 2 +- config/webpack.prod.js | 2 +- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/config/plugins.js b/config/plugins.js index f4ee382f..103f1a87 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -1,4 +1,6 @@ const path = require('path') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') +const svgoconfig = require('./svgo.config') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const { WebpackManifestPlugin } = require('webpack-manifest-plugin') const ESLintPlugin = require('eslint-webpack-plugin') @@ -15,7 +17,38 @@ const SpriteHashPlugin = require('./webpack-sprite-hash-plugin') module.exports = { get: function (mode) { + const isProduction = mode === 'production' + // A single instance: `optimization.minimizer` is only `apply()`'d when `minimize: true` + // (see webpack `WebpackOptionsApply.js`), so WebP `?as=webp` must live on the main + // `plugins` list to work with `yarn start` / dev. Image minify runs on `processAssets` + // from this same plugin in production only (keeps dev watch fast). + const imageMinimizerOptions = { + loader: true, + generator: [ + { + preset: 'webp', + implementation: ImageMinimizerPlugin.imageminGenerate, + options: { + plugins: ['imagemin-webp'], + }, + }, + ], + } + if (isProduction) { + imageMinimizerOptions.minimizer = { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + ['gifsicle', { interlaced: true }], + ['jpegtran', { progressive: true }], + ['optipng', { optimizationLevel: 5 }], + ['svgo', { svgoconfig }], + ], + }, + } + } const plugins = [ + new ImageMinimizerPlugin(imageMinimizerOptions), new WebpackThemeJsonPlugin({ watch: mode !== 'production', }), diff --git a/config/webpack.common.js b/config/webpack.common.js index acc3f6ef..c3886ef4 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -1,8 +1,6 @@ const path = require('path') const entries = require('./entries') -const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') -const svgoconfig = require('./svgo.config') module.exports = { entry: entries, @@ -14,31 +12,6 @@ module.exports = { }, optimization: { minimizer: [ - new ImageMinimizerPlugin({ - minimizer: { - implementation: ImageMinimizerPlugin.imageminMinify, - options: { - // Lossless optimization with custom option - // Feel free to experiment with options for better result for you - plugins: [ - ['gifsicle', { interlaced: true }], - ['jpegtran', { progressive: true }], - ['optipng', { optimizationLevel: 5 }], - // Svgo configuration here https://github.com/svg/svgo#configuratio - ['svgo', { svgoconfig }], - ], - }, - }, - generator: [ - { - preset: 'webp', - implementation: ImageMinimizerPlugin.imageminGenerate, - options: { - plugins: ['imagemin-webp'], - }, - }, - ], - }), new TerserPlugin({ parallel: true, terserOptions: { diff --git a/config/webpack.dev.js b/config/webpack.dev.js index 834f8892..fef55411 100644 --- a/config/webpack.dev.js +++ b/config/webpack.dev.js @@ -6,7 +6,7 @@ const loaders = require('./loaders') const mode = 'development' module.exports = merge(common, { - mode: mode, + mode, stats: 'errors-only', devtool: 'inline-source-map', devServer: { diff --git a/config/webpack.prod.js b/config/webpack.prod.js index 79b06500..aa6b4d66 100644 --- a/config/webpack.prod.js +++ b/config/webpack.prod.js @@ -5,7 +5,7 @@ const loaders = require('./loaders') const mode = 'production' module.exports = merge(common, { - mode: mode, + mode, stats: 'minimal', output: { filename: '[name].js', From e073cce30294294ce9d6d5fc2c8f4ab871816fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Mon, 27 Apr 2026 10:32:22 +0200 Subject: [PATCH 03/14] remove: delete unused background-static SCSS mixin --- src/scss/02-tools/_m-background-static.scss | 27 --------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/scss/02-tools/_m-background-static.scss diff --git a/src/scss/02-tools/_m-background-static.scss b/src/scss/02-tools/_m-background-static.scss deleted file mode 100644 index 99be8bba..00000000 --- a/src/scss/02-tools/_m-background-static.scss +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Background retina - Make an alignment (left or right) - * - * @author Cédric Andrietti - * - * @param $direction - * - * Examples : - * - * .test { - * @include background-static("your-image"); - * } - * - */ - -@mixin background-static($filename, $retina: true, $position: center center, $size: auto 100%, $type: "png") { - background-image: url(../img/static/#{$filename}.#{$type}?as=webp); - background-repeat: no-repeat; - background-position: $position; - background-size: $size; - - @if ($retina) { - @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { - background-image: url(../img/static/#{$filename}@2x.#{$type}?as=webp); - } - } -} From bcc4162b68186d4b6de55e0fcd28526cc8222705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Mon, 27 Apr 2026 11:58:40 +0200 Subject: [PATCH 04/14] Add WebpackStaticImagesPlugin to handle static image processing - Introduced WebpackStaticImagesPlugin to copy and convert static images to WebP format. - Configured plugin in plugins.js with input and output directories, quality settings, and console output options. --- config/plugins.js | 7 ++ config/webpack-static-images-plugin.js | 125 +++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 config/webpack-static-images-plugin.js diff --git a/config/plugins.js b/config/plugins.js index 103f1a87..c4fd0b23 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -14,6 +14,7 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl const WebpackImageSizesPlugin = require('./webpack-image-sizes-plugin') const WebpackThemeJsonPlugin = require('./webpack-theme-json-plugin') const SpriteHashPlugin = require('./webpack-sprite-hash-plugin') +const WebpackStaticImagesPlugin = require('./webpack-static-images-plugin') module.exports = { get: function (mode) { @@ -85,6 +86,12 @@ module.exports = { defaultImageFormat: 'jpg', // Generated image format (jpg, png, webp, avif) silence: true, // Suppress console output }), + new WebpackStaticImagesPlugin({ + inputDir: 'src/img/static', + outputDir: 'dist/images', + quality: 80, + silence: false, // Suppress console output + }), new MiniCssExtractPlugin({ filename: '[name].css', }), diff --git a/config/webpack-static-images-plugin.js b/config/webpack-static-images-plugin.js new file mode 100644 index 00000000..f955013e --- /dev/null +++ b/config/webpack-static-images-plugin.js @@ -0,0 +1,125 @@ +const fs = require('fs') +const path = require('path') + +// Try to require sharp, fallback gracefully if not available +let sharp +try { + sharp = require('sharp') +} catch (error) { + console.warn('⚠️ Sharp not available. WebP conversion will be disabled.') +} + +/** + * Webpack plugin to copy and automatically convert static images in a folder to WebP. + */ +class WebpackStaticImagesPlugin { + /** + * Creates an instance of WebpackStaticImagesPlugin. + * + * @param {Object} [options={}] - Configuration options + * @param {string} [options.inputDir='src/img/static'] - Input directory + * @param {string} [options.outputDir='dist/images'] - Output directory + * @param {number} [options.quality=80] - WebP compression quality + * @param {boolean} [options.silence=false] - Disable console output + */ + constructor(options = {}) { + this.options = { + inputDir: 'src/img/static', + outputDir: 'dist/images', + quality: 80, + silence: false, + ...options, + } + } + + /** + * Logs a message to the console if silence option is not enabled. + */ + log(level, ...args) { + if (!this.options.silence) { + console[level](...args) + } + } + + /** + * Entry point for Webpack. + */ + apply(compiler) { + if (!sharp) { + return + } // If Sharp is not installed, silently cancel. + + // Use afterEmit to ensure that Webpack has created the dist folder. + compiler.hooks.afterEmit.tapPromise('WebpackStaticImagesPlugin', async (compilation) => { + const { context } = compiler + const inputPath = path.resolve(context, this.options.inputDir) + const outputPath = path.resolve(context, this.options.outputDir) + + // Check if the source directory exists. + if (!fs.existsSync(inputPath)) { + this.log('warn', `⚠️ Source directory not found: ${inputPath}`) + return + } + + // Create the output directory if it doesn't exist. + if (!fs.existsSync(outputPath)) { + fs.mkdirSync(outputPath, { recursive: true }) + } + + try { + this.log('log', '🔄 Starting static images processing...') + await this.processImages(inputPath, outputPath) + this.log('log', '🎉 Static images processing completed!') + } catch (error) { + this.log('error', '❌ Error during static images processing:', error) + } + }) + } + + /** + * Processes the images in the directory. + */ + async processImages(inputPath, outputPath) { + const files = fs.readdirSync(inputPath) + const promises = [] + let count = 0 + + for (const file of files) { + // Only process JPG and PNG files. + if (file.match(/\.(png|jpe?g)$/i)) { + const filePath = path.join(inputPath, file) + const fileName = path.parse(file).name + + // Create the output paths. + const outputOriginal = path.join(outputPath, file) + const outputWebp = path.join(outputPath, `${fileName}.webp`) + + // Prepare the Sharp instances. + // (Sharp returns Promises, it is crucial to wait for them.) + const copyPromise = sharp(filePath).toFile(outputOriginal) + const webpPromise = sharp(filePath).webp({ quality: this.options.quality }).toFile(outputWebp) + + // Group the two actions for this file. + const filePromise = Promise.all([copyPromise, webpPromise]) + .then(() => { + this.log('log', ` ✅ Converted: ${file} (+ .webp version)`) + }) + .catch((err) => { + this.log('error', ` ❌ Error on ${file}:`, err.message) + }) + + promises.push(filePromise) + count++ + } + } + + // Wait for all images to be generated before returning to Webpack. + if (count > 0) { + await Promise.all(promises) + } else { + this.log('log', ' ℹ️ No images to process in the directory.') + } + } +} + +module.exports = WebpackStaticImagesPlugin From e66b2884aba11a69c6963ce8e612ccb6f9fcf10a Mon Sep 17 00:00:00 2001 From: mricoul Date: Mon, 27 Apr 2026 14:14:47 +0200 Subject: [PATCH 05/14] feat(webpack): optimize static image processing in watch mode Improve build performance by skipping image processing when no files in the input directory have changed. The plugin now registers the input directory as a context dependency and checks modified files during subsequent builds to avoid redundant processing. --- config/webpack-static-images-plugin.js | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/config/webpack-static-images-plugin.js b/config/webpack-static-images-plugin.js index f955013e..b6795075 100644 --- a/config/webpack-static-images-plugin.js +++ b/config/webpack-static-images-plugin.js @@ -30,6 +30,7 @@ class WebpackStaticImagesPlugin { silence: false, ...options, } + this.hasBeenBuiltOnce = false } /** @@ -49,6 +50,13 @@ class WebpackStaticImagesPlugin { return } // If Sharp is not installed, silently cancel. + compiler.hooks.compilation.tap('WebpackStaticImagesPlugin', (compilation) => { + const inputPath = path.resolve(compiler.context, this.options.inputDir) + if (fs.existsSync(inputPath)) { + compilation.contextDependencies.add(inputPath) + } + }) + // Use afterEmit to ensure that Webpack has created the dist folder. compiler.hooks.afterEmit.tapPromise('WebpackStaticImagesPlugin', async (compilation) => { const { context } = compiler @@ -61,6 +69,24 @@ class WebpackStaticImagesPlugin { return } + // Skip re-processing in watch when nothing under inputDir changed (see WebpackImageSizesPlugin). + let hasChanges = false + if (this.hasBeenBuiltOnce && compilation.modifiedFiles) { + for (const filePath of compilation.modifiedFiles) { + if (this.isFileUnderDir(filePath, inputPath)) { + hasChanges = true + break + } + } + } + + if (this.hasBeenBuiltOnce && !hasChanges) { + this.log('log', `✅ No changes detected in ${this.options.inputDir}`) + return + } + + this.hasBeenBuiltOnce = true + // Create the output directory if it doesn't exist. if (!fs.existsSync(outputPath)) { fs.mkdirSync(outputPath, { recursive: true }) @@ -76,6 +102,19 @@ class WebpackStaticImagesPlugin { }) } + /** + * Returns true if `filePath` is `inputDir` or a file inside it (cross-platform). + */ + isFileUnderDir(filePath, inputDirResolved) { + const resolvedFile = path.resolve(filePath) + const resolvedDir = path.resolve(inputDirResolved) + if (resolvedFile === resolvedDir) { + return true + } + const relative = path.relative(resolvedDir, resolvedFile) + return !relative.startsWith('..') && !path.isAbsolute(relative) + } + /** * Processes the images in the directory. */ From 269fafd36dd1b694b227a87a3ef778df57222395 Mon Sep 17 00:00:00 2001 From: mricoul Date: Mon, 27 Apr 2026 14:15:03 +0200 Subject: [PATCH 06/14] feat(webpack): use byte-for-byte copy for original images in static plugin Switch from Sharp to fs.copyFile for original assets to ensure they are preserved exactly as-is, maintaining source quality and metadata (EXIF/ICC). Sharp is now used exclusively for generating the WebP derivatives. --- config/webpack-static-images-plugin.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/config/webpack-static-images-plugin.js b/config/webpack-static-images-plugin.js index b6795075..e38dfe2f 100644 --- a/config/webpack-static-images-plugin.js +++ b/config/webpack-static-images-plugin.js @@ -10,7 +10,7 @@ try { } /** - * Webpack plugin to copy and automatically convert static images in a folder to WebP. + * Webpack plugin to copy static images byte-for-byte and generate WebP derivatives with Sharp. */ class WebpackStaticImagesPlugin { /** @@ -19,7 +19,7 @@ class WebpackStaticImagesPlugin { * @param {Object} [options={}] - Configuration options * @param {string} [options.inputDir='src/img/static'] - Input directory * @param {string} [options.outputDir='dist/images'] - Output directory - * @param {number} [options.quality=80] - WebP compression quality + * @param {number} [options.quality=80] - WebP output quality (originals are not re-encoded) * @param {boolean} [options.silence=false] - Disable console output */ constructor(options = {}) { @@ -133,15 +133,13 @@ class WebpackStaticImagesPlugin { const outputOriginal = path.join(outputPath, file) const outputWebp = path.join(outputPath, `${fileName}.webp`) - // Prepare the Sharp instances. - // (Sharp returns Promises, it is crucial to wait for them.) - const copyPromise = sharp(filePath).toFile(outputOriginal) + // Byte copy preserves source quality, EXIF/ICC, etc. WebP is generated separately. + const copyPromise = fs.promises.copyFile(filePath, outputOriginal) const webpPromise = sharp(filePath).webp({ quality: this.options.quality }).toFile(outputWebp) - // Group the two actions for this file. const filePromise = Promise.all([copyPromise, webpPromise]) .then(() => { - this.log('log', ` ✅ Converted: ${file} (+ .webp version)`) + this.log('log', ` ✅ ${file} (original copied, .webp generated)`) }) .catch((err) => { this.log('error', ` ❌ Error on ${file}:`, err.message) From 8f633305b2f3f6a39281e653372ed9a8ce0fd778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Tue, 28 Apr 2026 16:14:29 +0200 Subject: [PATCH 07/14] refactor: remove JPEG images and update to WebP format - Deleted unused JPEG images (default.jpg, logo.jpg) and replaced them with WebP versions (default.webp, logo.webp). - Updated SCSS and Webpack configuration to reference the new WebP images. - Removed imagemin-webp from package.json and yarn.lock as it is no longer needed. - Generate default images in webp format by default --- config/plugins.js | 50 +++++++++------------------ config/webpack-image-sizes-plugin.js | 7 ++-- src/img/static/default.jpg | Bin 6737 -> 0 bytes src/img/static/default.webp | Bin 0 -> 3930 bytes src/img/static/logo.jpg | Bin 6737 -> 0 bytes src/img/static/logo.webp | Bin 0 -> 3930 bytes src/scss/login.scss | 2 +- 7 files changed, 21 insertions(+), 38 deletions(-) delete mode 100644 src/img/static/default.jpg create mode 100644 src/img/static/default.webp delete mode 100644 src/img/static/logo.jpg create mode 100644 src/img/static/logo.webp diff --git a/config/plugins.js b/config/plugins.js index c4fd0b23..c28f9417 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -18,38 +18,7 @@ const WebpackStaticImagesPlugin = require('./webpack-static-images-plugin') module.exports = { get: function (mode) { - const isProduction = mode === 'production' - // A single instance: `optimization.minimizer` is only `apply()`'d when `minimize: true` - // (see webpack `WebpackOptionsApply.js`), so WebP `?as=webp` must live on the main - // `plugins` list to work with `yarn start` / dev. Image minify runs on `processAssets` - // from this same plugin in production only (keeps dev watch fast). - const imageMinimizerOptions = { - loader: true, - generator: [ - { - preset: 'webp', - implementation: ImageMinimizerPlugin.imageminGenerate, - options: { - plugins: ['imagemin-webp'], - }, - }, - ], - } - if (isProduction) { - imageMinimizerOptions.minimizer = { - implementation: ImageMinimizerPlugin.imageminMinify, - options: { - plugins: [ - ['gifsicle', { interlaced: true }], - ['jpegtran', { progressive: true }], - ['optipng', { optimizationLevel: 5 }], - ['svgo', { svgoconfig }], - ], - }, - } - } const plugins = [ - new ImageMinimizerPlugin(imageMinimizerOptions), new WebpackThemeJsonPlugin({ watch: mode !== 'production', }), @@ -81,9 +50,9 @@ module.exports = { outputImageLocations: 'image-locations.json', // Output locations file name outputImageSizes: 'image-sizes.json', // Output sizes file name generateDefaultImages: true, // Generate default images - defaultImageSource: 'src/img/static/default.jpg', // Source image for generation + defaultImageSource: 'src/img/static/default.webp', // Source image for generation defaultImagesOutputDir: 'dist/images', // Default images output directory - defaultImageFormat: 'jpg', // Generated image format (jpg, png, webp, avif) + defaultImageFormat: 'webp', // Generated image format (jpg, png, webp, avif) silence: true, // Suppress console output }), new WebpackStaticImagesPlugin({ @@ -101,6 +70,21 @@ module.exports = { ] if (mode === 'production') { + plugins.push( + new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + ['gifsicle', { interlaced: true }], + ['jpegtran', { progressive: true }], + ['optipng', { optimizationLevel: 5 }], + ['svgo', { svgoconfig }], + ], + }, + }, + }) + ) plugins.push( new BundleAnalyzerPlugin({ analyzerMode: 'json', diff --git a/config/webpack-image-sizes-plugin.js b/config/webpack-image-sizes-plugin.js index d7a9618f..1b30a9af 100644 --- a/config/webpack-image-sizes-plugin.js +++ b/config/webpack-image-sizes-plugin.js @@ -41,9 +41,9 @@ class WebpackImageSizesPlugin { outputImageLocations: 'image-locations.json', outputImageSizes: 'image-sizes.json', generateDefaultImages: false, - defaultImageSource: 'src/img/static/default.jpg', + defaultImageSource: 'src/img/static/default.webp', defaultImagesOutputDir: 'dist/images', - defaultImageFormat: 'jpg', + defaultImageFormat: 'webp', silence: false, ...options, } @@ -543,8 +543,7 @@ class WebpackImageSizesPlugin { this.log( 'log', - `🖼️ Processing ${sizeKeys.length} default images (${format.toUpperCase()}) from ${ - this.options.defaultImageSource + `🖼️ Processing ${sizeKeys.length} default images (${format.toUpperCase()}) from ${this.options.defaultImageSource }` ) diff --git a/src/img/static/default.jpg b/src/img/static/default.jpg deleted file mode 100644 index 82629726f9c830c6d8da5719583fa5757202211e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6737 zcmeI%L2JS=6bJAZBTi^X6PFr@BZU?1L9hks*`5UbT>BvV7CQ=KhaGp^^x`+k(&{0K zc-!s&atV*uCj1VMkhmXrB(Ys@*2EYQ^Ml-hUP(kz6h|VCg-#w5JyAlaNv7M#tTsNh zwfVG|=C&-C%d%`PgUKY3Pcj{iQYHN`YCKkEX3VoOvnsbf@@l>)iefRZ>#C~jrdfr* za33Ux{r%vJvlr?~#wBx~ly=*sFa~Zs5V#O%+ej*|k0Wo#^ScRK e(azDsu5C85dqxSq{yv>qm@n!d?o{w|#qS@41V0M^ diff --git a/src/img/static/default.webp b/src/img/static/default.webp new file mode 100644 index 0000000000000000000000000000000000000000..02b7b6f68cab62e9b4c625cddcd59c5e30e3008b GIT binary patch literal 3930 zcmWIYbaM;hXJ80-bqWXzuuyOVvNxPzoXed$ze1bjFt|g prQ&F9Fj`5DHV#G`MWb!S(KhmE-(a-Mgw|E8VEFg{Q47QH0{|3mSa1LU literal 0 HcmV?d00001 diff --git a/src/img/static/logo.jpg b/src/img/static/logo.jpg deleted file mode 100644 index 82629726f9c830c6d8da5719583fa5757202211e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6737 zcmeI%L2JS=6bJAZBTi^X6PFr@BZU?1L9hks*`5UbT>BvV7CQ=KhaGp^^x`+k(&{0K zc-!s&atV*uCj1VMkhmXrB(Ys@*2EYQ^Ml-hUP(kz6h|VCg-#w5JyAlaNv7M#tTsNh zwfVG|=C&-C%d%`PgUKY3Pcj{iQYHN`YCKkEX3VoOvnsbf@@l>)iefRZ>#C~jrdfr* za33Ux{r%vJvlr?~#wBx~ly=*sFa~Zs5V#O%+ej*|k0Wo#^ScRK e(azDsu5C85dqxSq{yv>qm@n!d?o{w|#qS@41V0M^ diff --git a/src/img/static/logo.webp b/src/img/static/logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..02b7b6f68cab62e9b4c625cddcd59c5e30e3008b GIT binary patch literal 3930 zcmWIYbaM;hXJ80-bqWXzuuyOVvNxPzoXed$ze1bjFt|g prQ&F9Fj`5DHV#G`MWb!S(KhmE-(a-Mgw|E8VEFg{Q47QH0{|3mSa1LU literal 0 HcmV?d00001 diff --git a/src/scss/login.scss b/src/scss/login.scss index 6a69ad0e..7f2726aa 100644 --- a/src/scss/login.scss +++ b/src/scss/login.scss @@ -1,7 +1,7 @@ // SCSS variables to customise the login page styles // The CSS file generated in dist/ by webpack is called automatically thanks to the WP Login Page mu-plugin present in the WP Skeleton. -$login-logo-path: "../../src/img/static/logo.jpg?as=webp"; +$login-logo-path: "../../src/img/static/logo.webp"; $login-logo-size: 312px 43px; $login-logo-height: 60px; $login-body-bg-color: #666; From eda3d626ba03f4abff146f4029dfe97351aeb8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Tue, 28 Apr 2026 16:16:50 +0200 Subject: [PATCH 08/14] refactor: remove WebpackStaticImagesPlugin and its configuration - Deleted WebpackStaticImagesPlugin to streamline image processing. - Removed related configuration from plugins.js, eliminating the static image processing setup. - This change simplifies the build process by relying on existing image handling methods. --- config/plugins.js | 7 -- config/webpack-static-images-plugin.js | 162 ------------------------- 2 files changed, 169 deletions(-) delete mode 100644 config/webpack-static-images-plugin.js diff --git a/config/plugins.js b/config/plugins.js index c28f9417..81aadbe8 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -14,7 +14,6 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl const WebpackImageSizesPlugin = require('./webpack-image-sizes-plugin') const WebpackThemeJsonPlugin = require('./webpack-theme-json-plugin') const SpriteHashPlugin = require('./webpack-sprite-hash-plugin') -const WebpackStaticImagesPlugin = require('./webpack-static-images-plugin') module.exports = { get: function (mode) { @@ -55,12 +54,6 @@ module.exports = { defaultImageFormat: 'webp', // Generated image format (jpg, png, webp, avif) silence: true, // Suppress console output }), - new WebpackStaticImagesPlugin({ - inputDir: 'src/img/static', - outputDir: 'dist/images', - quality: 80, - silence: false, // Suppress console output - }), new MiniCssExtractPlugin({ filename: '[name].css', }), diff --git a/config/webpack-static-images-plugin.js b/config/webpack-static-images-plugin.js deleted file mode 100644 index e38dfe2f..00000000 --- a/config/webpack-static-images-plugin.js +++ /dev/null @@ -1,162 +0,0 @@ -const fs = require('fs') -const path = require('path') - -// Try to require sharp, fallback gracefully if not available -let sharp -try { - sharp = require('sharp') -} catch (error) { - console.warn('⚠️ Sharp not available. WebP conversion will be disabled.') -} - -/** - * Webpack plugin to copy static images byte-for-byte and generate WebP derivatives with Sharp. - */ -class WebpackStaticImagesPlugin { - /** - * Creates an instance of WebpackStaticImagesPlugin. - * - * @param {Object} [options={}] - Configuration options - * @param {string} [options.inputDir='src/img/static'] - Input directory - * @param {string} [options.outputDir='dist/images'] - Output directory - * @param {number} [options.quality=80] - WebP output quality (originals are not re-encoded) - * @param {boolean} [options.silence=false] - Disable console output - */ - constructor(options = {}) { - this.options = { - inputDir: 'src/img/static', - outputDir: 'dist/images', - quality: 80, - silence: false, - ...options, - } - this.hasBeenBuiltOnce = false - } - - /** - * Logs a message to the console if silence option is not enabled. - */ - log(level, ...args) { - if (!this.options.silence) { - console[level](...args) - } - } - - /** - * Entry point for Webpack. - */ - apply(compiler) { - if (!sharp) { - return - } // If Sharp is not installed, silently cancel. - - compiler.hooks.compilation.tap('WebpackStaticImagesPlugin', (compilation) => { - const inputPath = path.resolve(compiler.context, this.options.inputDir) - if (fs.existsSync(inputPath)) { - compilation.contextDependencies.add(inputPath) - } - }) - - // Use afterEmit to ensure that Webpack has created the dist folder. - compiler.hooks.afterEmit.tapPromise('WebpackStaticImagesPlugin', async (compilation) => { - const { context } = compiler - const inputPath = path.resolve(context, this.options.inputDir) - const outputPath = path.resolve(context, this.options.outputDir) - - // Check if the source directory exists. - if (!fs.existsSync(inputPath)) { - this.log('warn', `⚠️ Source directory not found: ${inputPath}`) - return - } - - // Skip re-processing in watch when nothing under inputDir changed (see WebpackImageSizesPlugin). - let hasChanges = false - if (this.hasBeenBuiltOnce && compilation.modifiedFiles) { - for (const filePath of compilation.modifiedFiles) { - if (this.isFileUnderDir(filePath, inputPath)) { - hasChanges = true - break - } - } - } - - if (this.hasBeenBuiltOnce && !hasChanges) { - this.log('log', `✅ No changes detected in ${this.options.inputDir}`) - return - } - - this.hasBeenBuiltOnce = true - - // Create the output directory if it doesn't exist. - if (!fs.existsSync(outputPath)) { - fs.mkdirSync(outputPath, { recursive: true }) - } - - try { - this.log('log', '🔄 Starting static images processing...') - await this.processImages(inputPath, outputPath) - this.log('log', '🎉 Static images processing completed!') - } catch (error) { - this.log('error', '❌ Error during static images processing:', error) - } - }) - } - - /** - * Returns true if `filePath` is `inputDir` or a file inside it (cross-platform). - */ - isFileUnderDir(filePath, inputDirResolved) { - const resolvedFile = path.resolve(filePath) - const resolvedDir = path.resolve(inputDirResolved) - if (resolvedFile === resolvedDir) { - return true - } - const relative = path.relative(resolvedDir, resolvedFile) - return !relative.startsWith('..') && !path.isAbsolute(relative) - } - - /** - * Processes the images in the directory. - */ - async processImages(inputPath, outputPath) { - const files = fs.readdirSync(inputPath) - const promises = [] - let count = 0 - - for (const file of files) { - // Only process JPG and PNG files. - if (file.match(/\.(png|jpe?g)$/i)) { - const filePath = path.join(inputPath, file) - const fileName = path.parse(file).name - - // Create the output paths. - const outputOriginal = path.join(outputPath, file) - const outputWebp = path.join(outputPath, `${fileName}.webp`) - - // Byte copy preserves source quality, EXIF/ICC, etc. WebP is generated separately. - const copyPromise = fs.promises.copyFile(filePath, outputOriginal) - const webpPromise = sharp(filePath).webp({ quality: this.options.quality }).toFile(outputWebp) - - const filePromise = Promise.all([copyPromise, webpPromise]) - .then(() => { - this.log('log', ` ✅ ${file} (original copied, .webp generated)`) - }) - .catch((err) => { - this.log('error', ` ❌ Error on ${file}:`, err.message) - }) - - promises.push(filePromise) - count++ - } - } - - // Wait for all images to be generated before returning to Webpack. - if (count > 0) { - await Promise.all(promises) - } else { - this.log('log', ' ℹ️ No images to process in the directory.') - } - } -} - -module.exports = WebpackStaticImagesPlugin From a18e12f6f73fe4438914068ac2bc5ec06ba61d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Wed, 29 Apr 2026 15:11:55 +0200 Subject: [PATCH 09/14] refactor: update SVGO configuration for compatibility with SVGO 3 - Modified the SVGO configuration to align with SVGO 3 standards, replacing the old plugin format with the new preset-default structure. - Adjusted the plugins list in plugins.js to ensure proper integration of the updated svgoconfig. - This change enhances image optimization while maintaining compatibility with the latest SVGO version. --- config/plugins.js | 2 +- config/svgo.config.js | 70 ++++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/config/plugins.js b/config/plugins.js index 81aadbe8..9d20af85 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -72,7 +72,7 @@ module.exports = { ['gifsicle', { interlaced: true }], ['jpegtran', { progressive: true }], ['optipng', { optimizationLevel: 5 }], - ['svgo', { svgoconfig }], + ['svgo', svgoconfig], ], }, }, diff --git a/config/svgo.config.js b/config/svgo.config.js index b0450e0b..fd92a537 100644 --- a/config/svgo.config.js +++ b/config/svgo.config.js @@ -1,38 +1,40 @@ +/** + * SVGO 3+ config for imagemin-svgo / svgo-loader. + * + * Format: SVGO 3 rejects the v2 style `{ cleanupAttrs: true }` (no `name` on plugin objects). + * + * Why not list every plugin like the old file? + * - SVGO 3 expects either named plugins or `preset-default`, which bundles most optimizers + * (cleanupAttrs, removeComments, convertPathData, mergePaths, etc.) in a maintained order. + * Duplicating that full list in v3 `{ name, params }` form would be long and redundant. + * - Reference: https://svgo.dev/docs/preset-default/ + * + * Mapping from the previous v2 `{ plugin: boolean }` list: + * - `true` on a built-in → usually covered by `preset-default` (see link above). + * - `false` falls into two cases in SVGO 3: + * (A) Plugin is part of `preset-default` → turn it off with `params.overrides` + * (e.g. `removeViewBox: false` below — removeViewBox runs inside the preset by default). + * (B) Plugin is optional / not in the preset → do not list it; SVGO only runs plugins you + * declare. So “false” in v2 = omit the plugin name here. + * Examples from the old config: + * - removeDimensions: https://svgo.dev/docs/plugins/removeDimensions/ — optional; not in + * `preset-default`; we omit it so width/height stay (unless you add `'removeDimensions'`). + * - removeRasterImages: https://svgo.dev/docs/plugins/removeRasterImages/ — optional; + * not in `preset-default`; we omit it so embedded rasters are kept. + * - Extra plugins the old list enabled but preset does not include: + * `convertStyleToAttrs`, `prefixIds` — listed explicitly below (sprites / ID stability). + */ module.exports = { plugins: [ - { cleanupAttrs: true }, - { removeDoctype: true }, - { removeXMLProcInst: true }, - { removeComments: true }, - { removeMetadata: true }, - { removeTitle: true }, - { removeDesc: true }, - { removeUselessDefs: true }, - { removeEditorsNSData: true }, - { removeEmptyAttrs: true }, - { removeHiddenElems: true }, - { removeEmptyText: true }, - { removeEmptyContainers: true }, - { cleanupEnableBackground: true }, - { convertStyleToAttrs: true }, - { convertColors: true }, - { convertPathData: true }, - { convertTransform: true }, - { removeUnknownsAndDefaults: true }, - { removeNonInheritableGroupAttrs: true }, - { removeUselessStrokeAndFill: true }, - { removeUnusedNS: true }, - { cleanupIDs: true }, - { cleanupNumericValues: true }, - { moveElemsAttrsToGroup: true }, - { moveGroupAttrsToElems: true }, - { collapseGroups: true }, - { removeRasterImages: false }, - { mergePaths: true }, - { convertShapeToPath: true }, - { sortAttrs: true }, - { removeDimensions: false }, - { prefixIds: true }, - { removeViewBox: false }, + { + name: 'preset-default', + params: { + overrides: { + removeViewBox: false, + }, + }, + }, + 'convertStyleToAttrs', + 'prefixIds', ], } From 6ff65b49c9842d1113ccfcd58cde57b1cb1ac39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Wed, 29 Apr 2026 15:42:45 +0200 Subject: [PATCH 10/14] fix svgo config comment --- config/svgo.config.js | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/config/svgo.config.js b/config/svgo.config.js index fd92a537..9cafa52d 100644 --- a/config/svgo.config.js +++ b/config/svgo.config.js @@ -1,39 +1,15 @@ -/** - * SVGO 3+ config for imagemin-svgo / svgo-loader. - * - * Format: SVGO 3 rejects the v2 style `{ cleanupAttrs: true }` (no `name` on plugin objects). - * - * Why not list every plugin like the old file? - * - SVGO 3 expects either named plugins or `preset-default`, which bundles most optimizers - * (cleanupAttrs, removeComments, convertPathData, mergePaths, etc.) in a maintained order. - * Duplicating that full list in v3 `{ name, params }` form would be long and redundant. - * - Reference: https://svgo.dev/docs/preset-default/ - * - * Mapping from the previous v2 `{ plugin: boolean }` list: - * - `true` on a built-in → usually covered by `preset-default` (see link above). - * - `false` falls into two cases in SVGO 3: - * (A) Plugin is part of `preset-default` → turn it off with `params.overrides` - * (e.g. `removeViewBox: false` below — removeViewBox runs inside the preset by default). - * (B) Plugin is optional / not in the preset → do not list it; SVGO only runs plugins you - * declare. So “false” in v2 = omit the plugin name here. - * Examples from the old config: - * - removeDimensions: https://svgo.dev/docs/plugins/removeDimensions/ — optional; not in - * `preset-default`; we omit it so width/height stay (unless you add `'removeDimensions'`). - * - removeRasterImages: https://svgo.dev/docs/plugins/removeRasterImages/ — optional; - * not in `preset-default`; we omit it so embedded rasters are kept. - * - Extra plugins the old list enabled but preset does not include: - * `convertStyleToAttrs`, `prefixIds` — listed explicitly below (sprites / ID stability). - */ module.exports = { plugins: [ { name: 'preset-default', params: { overrides: { + // Disable a plugin included by default that you don't want (false) removeViewBox: false, }, }, }, + // Plugins that are not in the "preset-default" and that you want to activate 'convertStyleToAttrs', 'prefixIds', ], From 3004476e9e08fdb88ead953d4c5c394a279086f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Thu, 7 May 2026 14:09:03 +0200 Subject: [PATCH 11/14] feat(svgo): add 'removeTitle' plugin to SVGO configuration - Included the 'removeTitle' plugin in the SVGO configuration to enhance SVG optimization by removing title elements from SVG files. - This addition aims to streamline SVG output and reduce file size. --- config/svgo.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/config/svgo.config.js b/config/svgo.config.js index 9cafa52d..7e994e7a 100644 --- a/config/svgo.config.js +++ b/config/svgo.config.js @@ -10,6 +10,7 @@ module.exports = { }, }, // Plugins that are not in the "preset-default" and that you want to activate + 'removeTitle', 'convertStyleToAttrs', 'prefixIds', ], From 559d8e5a1501f4caa79ccc42ba78363eab8358f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Fri, 2 Oct 2026 12:49:59 +0200 Subject: [PATCH 12/14] update package-json lock --- package-lock.json | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/package-lock.json b/package-lock.json index 6edc34d7..b9e7ac36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "imagemin-jpegtran": "^7.0.0", "imagemin-optipng": "^8.0.0", "imagemin-svgo": "^10.0.1", + "imagemin-webp": "^8.0.0", "mini-css-extract-plugin": "^2.10.2", "postcss": "^8.5.6", "postcss-import": "^16.1.1", @@ -6038,6 +6039,27 @@ "node": ">=0.10.0" } }, + "node_modules/cwebp-bin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cwebp-bin/-/cwebp-bin-8.0.0.tgz", + "integrity": "sha512-j2s6jA84aG20lB0i/FBwqZGc8nHx4VASUK8OTDxy3xoUHoX/+pP6T15/TnWwhMcD0pZ05y5GgRPkurufOC8tnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.1" + }, + "bin": { + "cwebp": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/imagemin/cwebp-bin?sponsor=1" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "dev": true, @@ -8515,6 +8537,21 @@ "node": ">=10.13.0" } }, + "node_modules/imagemin-webp": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/imagemin-webp/-/imagemin-webp-8.0.0.tgz", + "integrity": "sha512-yN6kNKir6T/U3AtP3uLHrLn9XYafk2m49EbUqLCQ3GPRRLRs+4pUQxxaHz+lnTDM+LQpkSjGQaFVcSgYqvW3dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cwebp-bin": "^8.0.0", + "exec-buffer": "^3.2.0", + "is-cwebp-readable": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, "node_modules/imagemin/node_modules/file-type": { "version": "16.5.4", "dev": true, @@ -8791,6 +8828,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-cwebp-readable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-cwebp-readable/-/is-cwebp-readable-3.0.0.tgz", + "integrity": "sha512-bpELc7/Q1/U5MWHn4NdHI44R3jxk0h9ew9ljzabiRl70/UIjL/ZAqRMb52F5+eke/VC8yTiv4Ewryo1fPWidvA==", + "dev": true, + "license": "ISC", + "dependencies": { + "file-type": "^10.5.0" + } + }, + "node_modules/is-cwebp-readable/node_modules/file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/is-data-descriptor": { "version": "1.0.1", "dev": true, From 0f068df1fb2f576d20f4af22eb5dbeb0d1ae08fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Fri, 2 Oct 2026 12:53:35 +0200 Subject: [PATCH 13/14] fix bugbot (webpack): register ImageMinimizerPlugin in optimization.minimizer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move image minimization out of the plugins array so it runs in webpack’s optimization pipeline. Keeps SVGO config wired as svgoconfig for imagemin-svgo. --- config/plugins.js | 17 ----------------- config/webpack.common.js | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/config/plugins.js b/config/plugins.js index 9d20af85..05123095 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -1,6 +1,4 @@ const path = require('path') -const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') -const svgoconfig = require('./svgo.config') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const { WebpackManifestPlugin } = require('webpack-manifest-plugin') const ESLintPlugin = require('eslint-webpack-plugin') @@ -63,21 +61,6 @@ module.exports = { ] if (mode === 'production') { - plugins.push( - new ImageMinimizerPlugin({ - minimizer: { - implementation: ImageMinimizerPlugin.imageminMinify, - options: { - plugins: [ - ['gifsicle', { interlaced: true }], - ['jpegtran', { progressive: true }], - ['optipng', { optimizationLevel: 5 }], - ['svgo', svgoconfig], - ], - }, - }, - }) - ) plugins.push( new BundleAnalyzerPlugin({ analyzerMode: 'json', diff --git a/config/webpack.common.js b/config/webpack.common.js index c3886ef4..8f7d00a4 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -1,6 +1,8 @@ const path = require('path') const entries = require('./entries') +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') +const svgoconfig = require('./svgo.config') module.exports = { entry: entries, @@ -12,6 +14,19 @@ module.exports = { }, optimization: { minimizer: [ + new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + ['gifsicle', { interlaced: true }], + ['jpegtran', { progressive: true }], + ['optipng', { optimizationLevel: 5 }], + ['svgo', svgoconfig], + ], + }, + }, + }), new TerserPlugin({ parallel: true, terserOptions: { From 440fb54a711136b94818202af32d984384834cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Fri, 2 Oct 2026 14:13:58 +0200 Subject: [PATCH 14/14] chore: remove unused imagemin-webp and related dependencies from package.json and package-lock.json - Deleted imagemin-webp from both package.json and package-lock.json as it is no longer needed for the project. - Cleaned up the package-lock.json by removing associated entries to streamline dependency management. --- package-lock.json | 57 ----------------------------------------------- package.json | 1 - 2 files changed, 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9e7ac36..6edc34d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "imagemin-jpegtran": "^7.0.0", "imagemin-optipng": "^8.0.0", "imagemin-svgo": "^10.0.1", - "imagemin-webp": "^8.0.0", "mini-css-extract-plugin": "^2.10.2", "postcss": "^8.5.6", "postcss-import": "^16.1.1", @@ -6039,27 +6038,6 @@ "node": ">=0.10.0" } }, - "node_modules/cwebp-bin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cwebp-bin/-/cwebp-bin-8.0.0.tgz", - "integrity": "sha512-j2s6jA84aG20lB0i/FBwqZGc8nHx4VASUK8OTDxy3xoUHoX/+pP6T15/TnWwhMcD0pZ05y5GgRPkurufOC8tnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bin-build": "^3.0.0", - "bin-wrapper": "^4.0.1" - }, - "bin": { - "cwebp": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/imagemin/cwebp-bin?sponsor=1" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "dev": true, @@ -8537,21 +8515,6 @@ "node": ">=10.13.0" } }, - "node_modules/imagemin-webp": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/imagemin-webp/-/imagemin-webp-8.0.0.tgz", - "integrity": "sha512-yN6kNKir6T/U3AtP3uLHrLn9XYafk2m49EbUqLCQ3GPRRLRs+4pUQxxaHz+lnTDM+LQpkSjGQaFVcSgYqvW3dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cwebp-bin": "^8.0.0", - "exec-buffer": "^3.2.0", - "is-cwebp-readable": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/imagemin/node_modules/file-type": { "version": "16.5.4", "dev": true, @@ -8828,26 +8791,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-cwebp-readable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-cwebp-readable/-/is-cwebp-readable-3.0.0.tgz", - "integrity": "sha512-bpELc7/Q1/U5MWHn4NdHI44R3jxk0h9ew9ljzabiRl70/UIjL/ZAqRMb52F5+eke/VC8yTiv4Ewryo1fPWidvA==", - "dev": true, - "license": "ISC", - "dependencies": { - "file-type": "^10.5.0" - } - }, - "node_modules/is-cwebp-readable/node_modules/file-type": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", - "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-data-descriptor": { "version": "1.0.1", "dev": true, diff --git a/package.json b/package.json index 7adceded..3f1255d9 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "imagemin-jpegtran": "^7.0.0", "imagemin-optipng": "^8.0.0", "imagemin-svgo": "^10.0.1", - "imagemin-webp": "^8.0.0", "mini-css-extract-plugin": "^2.10.2", "postcss": "^8.5.6", "postcss-import": "^16.1.1",