Optimize images using webpack (caching)

Hey everyone,

I just started with webpack to make images (photos) smaller before I deploy them.
Now I’m stuck with a little problem. When I deploy the application I don’t want the images get optimized every time (from a big photo => a small photo). And want to cache the small photo somehow (so it checks if those exist in the first place).

The photo’s are getting a hash (based on the file data).

When I use this, it won’t cache

new ImageminPlugin({
            plugins: [
                imageminMozjpeg({
                    cacheFolder: '/cache',
                    quality: 60,
                    progressive: true
                })
            ]
        })

And when I use this

cacheFolder: resolve('./cache'), // use an existing folder called cache in the current dir

It is crashing on the resolve function

How can I cache the images (so it don’t get re-optimized, when not necessary)

const SiteConfig = require('../../../resources/config.js');
const Webpack = require('webpack');
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const StyleLintPlugin = require('stylelint-webpack-plugin');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const WebpackAssetsManifest = require('webpack-assets-manifest');
const TerserPlugin = require('terser-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default
const imageminMozjpeg = require('imagemin-mozjpeg');
const resolve = require('resolve');

const devMode = process.env.NODE_ENV !== 'production';

let alias = {
    'jquery': path.join(__dirname, '../../jquery/dist/jquery.js'), // Necessary otherwise bootstrap will use own jquery
    'masonry': 'masonry-layout',
    'isotope': 'isotope-layout',
};

if (typeof SiteConfig.alias === 'object') {
    alias = Object.assign(alias, SiteConfig.alias);
}

module.exports = {
    entry: SiteConfig.entry,
    output: {
        path: path.resolve(__dirname, '../../../assets'),
        filename: devMode ? '[name].js' : '[name].[contenthash].js',
        sourceMapFilename: "[file].map",
    },
    devtool: 'source-map',
    optimization: {
        minimizer: [
            new TerserPlugin({
                extractComments: true,
            })
        ],
        splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    },
    module: {
        rules: [
            {
                test: /\.js?$/,
                exclude: /node_modules/,
                loaders: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-env'],
                        }
                    },
                    {
                        loader: "eslint-loader",
                        options: {
                            configFile: path.resolve(__dirname, '.eslintrc'),
                            ignorePath: path.resolve(__dirname, '.eslintignore')
                        }
                    }
                ],
            },
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: "css-loader",
                        options: {
                            sourceMap: devMode ? true : false,
                            importLoaders: 2,
                        }
                    }, {
                        loader: "postcss-loader",
                        options: {
                            config: {
                                path: path.resolve(__dirname, 'postcss.config.js')
                            },
                            sourceMap: devMode ? true : false,
                        }
                    }, {
                        loader: "sass-loader",
                        options: {
                            sourceMap: devMode ? true : false,
                        }
                    }
                ],
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name].[hash].[ext]',
                            context: './resources',
                            outputPath: 'img',
                        }
                    }
                ]

            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                include: /node_modules/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name].[hash].[ext]',
                            outputPath: 'img/modules/',
                        }
                    }
                ]
            },
            {
                test: /\.(woff|woff2|eot|ttf)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: "fonts/[name].[ext]"
                        }
                    }
                ]
            },
            {
                test: require.resolve('jquery'),
                use: [{
                    loader: 'expose-loader',
                    options: 'jQuery'
                }]
            }
        ]
    },
    resolve: {
        alias: alias,
    },
    plugins: [
        new Webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery',
            Popper: ['popper.js', 'default'],
            Tether: 'tether',
            'window.Tether': 'tether'
        }),
        new MiniCssExtractPlugin({
            filename: devMode ? '[name].css' : '[name].[contenthash].css',
            chunkFilename: devMode ? '[id].css' : '[id].[contenthash].css',
        }),
        new CleanWebpackPlugin(
            ['assets'],
            {
                beforeEmit: true,
                root: path.resolve(__dirname, '../../../'),
            }
        ),
        new StyleLintPlugin({
            configFile: path.resolve(__dirname, '.stylelintrc')
        }),
        new BrowserSyncPlugin({
            proxy: SiteConfig.proxyUrl
        }),
        new Webpack.LoaderOptionsPlugin({
            // debug: true
        }),
        new WebpackAssetsManifest({}),
        new ImageminPlugin({
            plugins: [
                imageminMozjpeg({
                    cacheFolder: '/cache',
                    quality: 60,
                    progressive: true
                })
            ]
        })
    ]
};

Where is ./cache located in refence to your webpack.config?

Generally I would say that using webpack in this manner is bad practice. Webpack works best when it builds to an empty folder and if you want to have it be your image minifier, then you might be better off with a different solution.

I haven’t done that yet I think (to be honest, I don’t know if its the right command).
The reason I want to ‘cache’ it. Is because of the deployment time (why should you make an image smaller again). It will cost more time.