Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack mini-css-extract-plugin @font-face url resolution issue

I am facing difficulty understanding the nature of URLs/paths and how they are resolved while using the mini-css-extract-plugin, file-loader and configuring their options such as the context and filename properties.

I am developing a custom theme for WordPress, with the custom theme's folder being where I have initialized the npm project/package.json file and webpack.config.js file. I am using the browser-sync plugin to proxy the local WordPress server to the browser-sync server.

The file-loader is emitting the files into the dist>assets>fonts folder. But the URL being generated in the compiled CSS file is not being resolved correctly.

output CSS, browser console error

GET http://localhost:4444/wp-content/themes/test/dist/css/assets/fonts/my-font.ttf net::ERR_ABORTED 404 (Not Found)

GET http://localhost:4444/wp-content/themes/test/dist/css/assets/fonts/my-font.woff 404 (Not Found)

In the error above the url shows dist/css/assets/fonts/my-font.woff, which is not what I expected. It was expected to be dist/assets/fonts/my-font.woff since that is where file-loader emits the files in the dist folder. (as defined in the file-loader context option)

I don't understand why it is adding the css directory to the fonts url. The only mention of the dist/css path is in the filename property of the MiniCSSExtractPlugin options. Since that is where i would like the css file to be output.

This can be confirmed by removing the css path prefix on the filename property. The url then resolves correctly relative to the dist path, but then the compiled css file is placed in the root of the dist folder instead of its own dist/css sub-directory.

mini css extract plugin options

new MiniCSSExtractPlugin({
            filename: 'css/[name].css',
        }),

font-face rule in scss file

@font-face {
  font-family: 'my-font';
  src: url("../assets/fonts/my-font.eot");
  src: url("../assets/fonts/my-font.eot") format("embedded-opentype"),
  url("../assets/fonts/my-font.ttf") format("truetype"),
  url("../assets/fonts/my-font.woff") format("woff"),
  url("../assets/fonts/my-font.svg") format("svg");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

webpack config entry and output

 entry: {
        'frontend': './src/js/frontend.js',
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "js/[name].js"
    },

module.rules

module: {
        rules: [
            {...},
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    {
                        loader: MiniCSSExtractPlugin.loader
                    },
                    {
                        loader: "css-loader"
                    },
                    {
                        loader: "sass-loader"
                    },
                ]
            },
            {
                test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            context: path.resolve(__dirname, 'src'),
                            name: '[path][name].[ext]'
                        }
                    }
                ]
            }

plugins

plugins: [
        new CleanWebpackPlugin(),
        new BrowserSyncWebpackPlugin({...}),
        new MiniCSSExtractPlugin({
            filename: 'css/[name].css',
        }),
    ]

I am a beginner at webpack and I am facing several difficulties trying to get font-face URLs to resolve correctly. I have a feeling that I don't understand the purpose of relative/absolute paths, context and publicpath in the webpack config. How is the URL in the compiled CSS is being determined?

like image 540
nooberson Avatar asked Nov 07 '22 09:11

nooberson


1 Answers

I came across your question when I was having the same issue. I eventually managed to resolve it by using the publicPath option for the MiniCssExtractPlugin.

My directory structure looks like this, where assets contains the working files and build contains the compiled files. The webroot of my project is /src.

/src
  /assets
    /css
    /webfonts
  /build
    /css
    /webfonts

Therefore, by setting an absolute path for publicPath, it meant the font files in my CSS were referenced by the absolute file path, rather than a relative file path.

{
  loader: MiniCssExtractPlugin.loader,
  options: {
    publicPath: '/build/',
  },
},
like image 142
fubar Avatar answered Nov 22 '22 11:11

fubar