Webpack 5: file-loader generates a copy of fonts with hash-name
Asked Answered
V

6

35

I cant figure out whats going on here. I use the file-loader to load the fonts of my application:

{
    test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
    use: [{
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath: 'assets/fonts/',
        publicPath: '../fonts/'
      }
    }]
  },

The fonts are also generated in the structure I specified: dist/assets/fonts. outputPath: 'assets/fonts/' ... it seems to work correctly.

But: Webpack also packs the fonts with a hash as a name under /dist and sets the path in the CSS file to these files.

@font-face{font-family:"PaulGrotesk";font-style:normal;font-weight:400;src:url(../../d9bc4e30281429c0cddd.eot);

strange font files

What is happening here? How can I prevent the additional files from being generated and used as a path?

I am using

  • webpack: ^5.47.1
  • webpack-cli: ^4.7.2

My webpack.config:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {

  entry: './src/index.js',

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'assets/js/bundle_v1.0.0.js'
  },

  module: {

    rules: [

      {
        test: /\.js?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        include: path.join(__dirname, 'index'),
        options: {
          presets: ['@babel/preset-env']
        }
      },

      {
        test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'assets/fonts/',
            publicPath: '../fonts/'
          }
        }]
      },

      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
            }
          }
        ]
      },

      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },

    ]
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'node_modules'),
      /* https://webpack.js.org/configuration/resolve/ */
    },
  },
  plugins: [

    new MiniCssExtractPlugin({
      filename: 'assets/css/bundle_v1.0.0.css'
    }),

    new HtmlWebpackPlugin({
      inject: 'body',
      template: './src/index.html',
      filename: 'index.html',
    })

  ]

};
Vin answered 3/8, 2021 at 10:12 Comment(1)
Hey @toge - I had the same issue and have posted my answer below, hopefully is of some help!Warrin
W
58

I had the exact same issue as the OP (@toge) having migrated from Webpack 4 to 5. Whilst I was looking to implement the solution posted by @seanbun, I came across the following setup for the new Webpack 5 asset modules config that allowed me to get both my previous file-loader svgs and font files to generate correctly without duplication in the root output folder:

{
    test: /\.(woff(2)?|ttf|eot)$/,
    type: 'asset/resource',
    generator: {
        filename: './fonts/[name][ext]',
    },
},

This tells webpack to handle it via the asset modules config but output to a custom filepath, similar to how the file-loader config used to work.

This setup also removed my need to downgrade css-loader too which was an added benefit

Warrin answered 3/9, 2021 at 8:27 Comment(7)
Yes, that works for me!Brigettebrigg
this is the winner, thanksAeolotropic
This solved my issues when going from webpack 4 to webpack5Godding
Wow! What a keen eye you have. I swear I have read through all the webpack documentation several times by now. Thank you for spotting this webpack.js.org/guides/asset-management/#loading-fonts Unsure where you found the generator part? It wasn't very clear Maybe webpack.js.org/configuration/module/#rulegeneratorfilenamePensive
@harry-fin You're amazing. Thank you so much! After hours of soul-searching and gut-wrenching later, THIS WORKED FOR ME TOO!!!Cable
Yes, that works for me as well. thank you so much.Buckjump
I had the same issue and resolved it with this answer.Jagir
T
11

Apparently, Webpack 5 has a Asset Modules function which allows one to use asset files (fonts, icons, etc) without configuring additional loaders i.e. file-loader, url-loader and etc. The Asset Modules provides four types to handle different type of asset. You can find more details at https://webpack.js.org/guides/asset-modules/.

So, when you add below rule for font files, the files are acutally handled by the Asset Modules as well as the file-loader.

    {
        test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'assets/fonts/',
            publicPath: '../fonts/'
          }
        }]
    }

In this case, we want to use our own file-loader and disable the Asset Module. We can set the type to 'javascript/auto'. This would follow your path setting and keep the font files in the assets/fonts folder.

    {
        test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
        **type: "javascript/auto",**
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'assets/fonts/',
            publicPath: '../fonts/'
          }
        }]
    }

P.S I was using css-loader 6.2.0 and had an error - url.replace is not a function. I downgraded to css-loader 5.2.7 to resolve the issue.

Teachin answered 18/8, 2021 at 6:4 Comment(1)
in my case, svg files are also copied to fonts folder for some reason, i don't want them in my dist, only the fonts. is there a way to do that?Bordure
S
3

I've had a similar problem for a while and for my case here is how I solved it by just adding two lines of code. type: "javascript/auto" and esModule: false

{
test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
exclude: [IMG_DIR, /node_modules/],
type: "javascript/auto",
use: {
  loader: "file-loader",
  options: {
    name: "[name].[ext]",
    publicPath: "../assets/fonts/",
    esModule: false,
  }

I hope this solves it :)

Skimpy answered 23/1, 2023 at 20:2 Comment(0)
C
0

Loaders are applied from last to first. So my guess is that placing the file loader below the css/sass loaders could fix the issue.

The fonts may not be registered this way when needed by the css loader and therefore loaded twice.

Crumpled answered 3/8, 2021 at 10:37 Comment(1)
No, that does not work unfortunately. I found out that it works with webpack 4. Maybe that's the approach.Vin
D
0

I already had my font files in a serveable location so I didn't need to copy the assets. (I'm confused why webpack did this by default). My solution was to ignore those files by:

    new webpack.IgnorePlugin({
      resourceRegExp: /\.(woff(2)?|ttf|eot|svg)$/
    }),
Demetricedemetris answered 11/11, 2021 at 23:42 Comment(0)
S
0

I had similar problem but in my case problem was solved by "type:'asset/inline'"

   {
      test: /\.(woff|woff2|ttf|otf|eot)$/,
      type:'asset/inline',
      exclude: /node_modules/,
      loader: 'file-loader',
      options: {          
        esModule: false,
        name: '[name].[ext]',
        outputPath: '../assets/fonts/',
        publicPath: '../assets/fonts/'
      }
    },
Sheepshanks answered 3/5, 2022 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.