webpack 5 images/fonts in scss not compiling correctly

Viewed 1026

My issue is; images and fonts inside of my .scss file are getting renamed as hash and the url path in the scss file is getting changed. My .scss file is imported into my app.js at the very top of the file like:

import '../scss/app.scss';

Example in my .scss file:

@font-face {
  font-family: 'FTBase';
  src: url("../fonts/FTBase-Book.woff2") format("woff2"),
    url("../fonts/FTBase-Book.woff") format("woff");
  font-weight: 350;
  font-style: normal;
  font-display: swap
}
.event--1 {
  background: var(--salmon) url("../images/leading-women.jpg") no-repeat center;
  background-size: contain;
}

The output after I run dev/build/production is:

@font-face {
  font-family: "FTBase";
  src: url(../51ebe4030489fa0868f9.woff2) format("woff2"), url(../be034b081210fbdf38a2.woff) format("woff");
  font-weight: 350;
  font-style: normal;
  font-display: swap;
}
.event--2 {
  background: var(--salmon) url(../485639d38ea610d7bba2.jpg) no-repeat center;
  background-size: cover;
}

and it outputs the images/fonts into /dist.

Here is my webpack files: webpack.config.js:

const templateFiles = fs.readdirSync(environment.paths.source)
  .filter((file) => path.extname(file).toLowerCase() === '.html');

const htmlPluginEntries = templateFiles.map((template) => new HTMLWebpackPlugin({
  inject: 'body',
  hash: false,
  minify: false,
  filename: template,
  template: path.resolve(environment.paths.source, template),
  favicon: path.resolve(environment.paths.source, 'images', 'favicon.ico'),
}));

module.exports = {
  entry: {
    app: path.resolve(environment.paths.source, 'js', 'app.js'),
  },
  output: {
    filename: 'js/[name].js',
    path: environment.paths.output,
  },
  module: {
    rules: [{
        test: /\.((c|sa|sc)ss)$/i,
        use: [{
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: '../',
          }
        }, {
          loader: 'css-loader',
        }, {
          loader: 'sass-loader',
        }, {
          loader: 'postcss-loader',
        }],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
      {
        test: /\.(png|gif|jpe?g|svg|jpg)$/i,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'images/',
          },
        }, ],
      },
      {
        test: /\.(eot|ttf|woff|woff2)$/,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/',
          },
        }, ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name].min.css',
    }),
    new ImageminWebpWebpackPlugin({
      config: [{
        test: /\.(jpe?g|png)/,
        options: {
          quality: 76,
        }
      }],
      overrideExtension: true,
      detailedLogs: false,
      silent: false,
      strict: true,
    }),
    new CleanWebpackPlugin({
      verbose: true,
      cleanOnceBeforeBuildPatterns: ['**/*', '!stats.json'],
    }),
    new CopyWebpackPlugin({
      patterns: [{
        from: path.resolve(environment.paths.source, 'images'),
        to: path.resolve(environment.paths.output, 'images'),
        toType: 'dir',
        globOptions: {
          ignore: ['*.DS_Store', 'Thumbs.db'],
        },
      }, ],
    }),
  ].concat(htmlPluginEntries),
  target: 'web',
};

webpack.dev.config.js:

const webpackConfiguration = require('../webpack.config');
const environment = require('./environment');

module.exports = merge(webpackConfiguration, {
  mode: 'development',

  /* Manage source maps generation process */
  devtool: 'eval-source-map',

  /* Development Server Configuration */
  devServer: {
    contentBase: environment.paths.output,
    watchContentBase: true,
    publicPath: '/',
    open: true,
    historyApiFallback: true,
    compress: true,
    overlay: true,
    hot: false,
    watchOptions: {
      poll: 300,
    },
    ...environment.server,
  },

  /* File watcher options */
  watchOptions: {
    aggregateTimeout: 300,
    poll: 300,
    ignored: /node_modules/,
  },

  /* Additional plugins configuration */
  plugins: [],
});

webpack.prod.config.js:

const webpackConfiguration = require('../webpack.config');

module.exports = merge(webpackConfiguration, {
  mode: 'production',

  /* Manage source maps generation process. Refer to https://webpack.js.org/configuration/devtool/#production */
  devtool: false,

  /* Optimization configuration */
  optimization: {
    splitChunks: {
      chunks: 'all',
      name: 'vendor',
    },
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
      }),
      new CssMinimizerPlugin(),
    ],
  },

  /* Performance treshold configuration values */
  performance: {
    maxEntrypointSize: 512000,
    maxAssetSize: 512000,
  },

  /* Additional plugins configuration */
  plugins: [],
});

In my webpack.config.js file I have tried setting sourceMaps to true on css-loader & sass-loader, change publicPath and a few other things I tried from other forums with no luck.

1 Answers

So I figured it out, I had to remove file-loader from the images and fonts. I replaced it to this:

{
   test: /\.(png|jpg|gif|svg)$/,
   type: 'asset/resource',
}, {
   test: /.(ttf|otf|eot|woff(2)?)(\?[a-z0-9]+)?$/,
   type: 'asset/resource',
},

and then I had to edit the output to this:

  output: {
    filename: 'js/[name].js',
    path: environment.paths.output,
    assetModuleFilename: 'assets/[name][ext]',
  },

So now when I run dev/build/production it grabs all the assets that are in the scss file, keeps them the same name and creates a folder called 'assets' which is located '/dist/assets' and it automatically changes the url in the scss file to '../assets/image.jpg'

Related