const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const EsLintPlugin = require('eslint-webpack-plugin'); const StylelintPlugin = require('stylelint-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = (env) => ({ mode: env.production ? 'production' : 'development', target: 'web', entry: './src/js/index.js', output: { filename: env.production ? '[name].[contenthash].bundle.js' : '[name].bundle.js', path: path.resolve(__dirname, 'dist'), }, devtool: env.production ? 'cheap-source-map' : 'inline-source-map', devServer: { contentBase: './dist', hot: true, host: '0.0.0.0', }, plugins: [ new MiniCssExtractPlugin({ filename: env.production ? '[name].[contenthash].css' : '[name].css', }), new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // weird loader string syntax because https://github.com/bazilio91/ejs-compiled-loader/issues/46 template: '!!ejs-compiled-loader?{}!./src/templates/index.ejs', }), new EsLintPlugin(), new StylelintPlugin(), ], module: { rules: [ { test: /\.ejs$/, use: { // weird loader string syntax because https://github.com/bazilio91/ejs-compiled-loader/issues/46 // NOTE - talso an alternative workaround that involves passing // the usual 'ejs-compiled-loader' as the loader and passing // an empty options object, however hmr did not work with that fix. loader: '!!ejs-compiled-loader?{}!', }, }, { test: /\.js?/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], }, }, }, { test: /\.inline\.css$/i, include: path.resolve(__dirname, 'src/css'), use: [ { loader: 'style-loader', options: { insert: function insertAtTop(element) { const parent = document.querySelector('head'); // eslint-disable-next-line no-underscore-dangle const lastInsertedElement = window._lastElementInsertedByStyleLoader; if (!lastInsertedElement) { parent.insertBefore(element, parent.firstChild); } else if (lastInsertedElement.nextSibling) { parent.insertBefore(element, lastInsertedElement.nextSibling); } else { parent.appendChild(element); } // eslint-disable-next-line no-underscore-dangle window._lastElementInsertedByStyleLoader = element; }, }, }, 'css-loader', 'postcss-loader'], }, { test: /\.css$/i, include: path.resolve(__dirname, 'src/css'), exclude: /\.inline\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], }, { test: /\.scss$/i, include: path.resolve(__dirname, 'src/sass'), use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'], }, { test: /.(png|svg|jpg|jpeg|gif)$/i, include: path.resolve(__dirname, 'src'), type: 'asset', }, ], }, optimization: { minimize: true, minimizer: [ // For webpack@5 the `...` syntax extends existing minimizers // `...`, new CssMinimizerPlugin(), ], }, });