1

I'm trying to understand how to load fonts via fontawsome when loading them through a scss file:

this is my webpack config:

const path                      = require('path');
const webpack                   = require('webpack');
const UrlLoader                 = require('url-loader');
const BrowserSyncPlugin         = require('browser-sync-webpack-plugin');
const VueLoaderPlugin           = require('vue-loader/lib/plugin');
const MiniCssExtractPlugin      = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin   = require('optimize-css-assets-webpack-plugin');

publicFolder                    = path.resolve(__dirname, 'public');
// appFolder                        = path.resolve(__dirname, 'app');

module.exports = {

    entry: { 
                // Selects main js file
                main:   './public/es6/index.js'
    },

    output: {
                // Main path for the js folder
                path:       path.resolve(__dirname, 'public/js/'),
                // Select teh name the main js file (after compression)
                filename:   'bundle.js',
                // Public path 
                // publicPath: 'http://localhost:8080', 
                publicPath: '/public/js/',
                // Name the chunkFile (in case of external scripts)
                chunkFilename: '[name].[contenthash].js'
    },

    module: {
                rules: [    // Vue Files 
                            {
                                test: /\.vue$/,
                                exclude: /node_modules/, 
                                loader: 'vue-loader', 
                                options: {
                                            loader: {
                                                scss: 'vue-style-loader!css-loader!sass-loader', 
                                                css: 'vue-style-loader!css-loader'
                                            }
                                }
                            },
                            // JS files 
                            {
                                test:       /\.js$/,
                                exclude:    /node_modules/,
                                use: {
                                        loader: "babel-loader"
                                }
                            },
                            // CSS / SASS files 
                            {
                                test: /\.(sa|sc|c)ss$/,
                                // test: /\.scss$/,
                                use: [
                                        {
                                            loader: MiniCssExtractPlugin.loader,
                                        },
                                        {
                                            loader: 'css-loader',
                                            options: {
                                                        url: false,
                                                        minimize: true,
                                                        sourceMap: true
                                            }
                                        },
                                        {
                                            loader: 'postcss-loader'
                                        },
                                        {
                                            loader: 'sass-loader',
                                            options: {
                                                        sourceMap: true, 
                                                        minimize: true
                                            }
                                        }
                                ]
                            },
                            // Forgot why I need this... 
                            {
                                test: /\.(png|gif|jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)/,
                                use: [{
                                    loader: 'url-loader',
                                    options: {
                                      limit: 100000
                                    }
                                }]
                            }
                ]
    },
    plugins: [  
                // Load jQuery globally 
                new webpack.ProvidePlugin({
                    $: 'jquery',
                    jQuery: 'jquery',
                    'window.jQuery': 'jquery'
                }),

                // Hot module
                // new webpack.HotModuleReplacementPlugin(),

                // BrowserSync: Load page automatically on change
                new BrowserSyncPlugin({
                    proxy: 'https://potato.mywebsite.com/', 
                    port: 3000, 
                    files: [
                        '**/*.php'
                    ], 
                    ghostMode: {
                        clicks: false, 
                        location: false, 
                        forms: false, 
                        scroll: false
                    }, 
                    minify: false,
                    injectChanges: true, 
                    logFileChanges: true, 
                    logLevel: 'debug', 
                    logPrefix: 'webpack', 
                    notify: true, 
                    reloadDelay: 0
                }),

                // Provides a way to customize how progress is reported during a compilation
                new webpack.ProgressPlugin(),

                // Loads Vue
                new VueLoaderPlugin(), 

                // For webpack-dev-server (currently not in use)
                // new webpack.HotModuleReplacementPlugin(), 

                // Handle css in different files (scss file in login.js for example to a hashed login.css file)
                new MiniCssExtractPlugin({ filename: '../css/[name].css' }), 

                // CSS assets during the Webpack build and will optimize \ minimize the CSS
                new OptimizeCSSAssetsPlugin({}),

                // Not sure if needed yet
                new webpack.NamedModulesPlugin()

    ],
    devServer: {
                    // https:       true,
                    headers:        { 'Access-Controll-Allow-Origin': '*' },
                    compress:       true, 
                    quiet:          true, 
                    hot:            true,
                    inline:         true
    }

};

And this is my SCSS file where I load FontAwesome (and others)..

@import 'variable';

// Colors
@import 'colors/default';
@import 'colors/green';
@import 'colors/megna';
@import 'colors/purple';
@import 'colors/red';
@import 'colors/blue';
@import 'colors/blue-dark';
@import 'colors/default-dark';
@import 'colors/green-dark';
@import 'colors/red-dark';
@import 'colors/megna-dark';
@import 'colors/purple-dark';  


// Import Bootstrap source files
@import "../../node_modules/bootstrap/scss/bootstrap";


// This is for the icons
@import '../assets/icons/font-awesome/css/fontawesome-all.css';
@import '../assets/icons/simple-line-icons/css/simple-line-icons.css';
@import '../assets/icons/weather-icons/css/weather-icons.min.css';
@import '../assets/icons/themify-icons/themify-icons.css'; 
@import '../assets/icons/flag-icon-css/flag-icon.min.css';
@import "../assets/icons/material-design-iconic-font/css/material-design-iconic-font.min.css";


// This is the core files
@import 'core/core';
@import 'widgets/widgets';
@import 'responsive';


// In This scss you can write your scss
@import 'custom'; 

When running npm run dev (or others) i don't get any error mentioning fonts.

When loading my website I get these URLS refrences in the "network" pannel:

https://mywebsite.potato.com/public/webfonts/fa-regular-400.woff
https://mywebsite.potato.com/public/fonts/Simple-Line-Icons.ttf?-i3a2kk

pointing on font files that don't even exist in my directory (or at least do't get created..)

enter image description here

How do I load fonts properly?

EDIT:

Adding this for @FabioCosta

{
    test: /\.(png|gif|jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)/,
    use: [
            {
                loader: 'file-loader',
                options: {
                    name:       '[name].[ext]',
                    // name:        '[path][name].[ext]',
                    outputPath: '/public/fonts/',
                    publicPath: '/public/fonts/'
                }
             }
    ]
}

Adding the full module part:

module: {
            rules: [    // Vue Files 
                        {
                            test: /\.vue$/,
                            exclude: /node_modules/, 
                            loader: 'vue-loader', 
                            options: {
                                        loader: {
                                            scss: 'vue-style-loader!css-loader!sass-loader', 
                                            css: 'vue-style-loader!css-loader'
                                        }
                            }
                        },
                        // JS files 
                        {
                            test:       /\.js$/,
                            exclude:    /node_modules/,
                            use: {
                                    loader: "babel-loader"
                            }
                        },
                        // CSS / SASS files 
                        {
                            test: /\.(sa|sc|c)ss$/,
                            // test: /\.scss$/,
                            use: [
                                    {
                                        loader: MiniCssExtractPlugin.loader,
                                    },
                                    {
                                        loader: 'css-loader',
                                        options: {
                                                    url: false,
                                                    minimize: true,
                                                    sourceMap: true
                                        }
                                    },
                                    {
                                        loader: 'postcss-loader'
                                    },
                                    {
                                        loader: 'sass-loader',
                                        options: {
                                                    sourceMap: true, 
                                                    minimize: true
                                        }
                                    }
                            ]
                        },
                        {
                            test: /\.(png|gif|jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)/,
                            use: [
                                    {
                                        loader: 'file-loader',
                                        options: {
                                            // name:        '[path][name].[ext]',
                                            name:       '[name].[ext]',
                                            outputPath: '/public/fonts/',
                                            publicPath: '/public/fonts/'
                                        }
                                     }
                            ]
                        }

Tried following: https://chriscourses.com/blog/loading-fonts-webpack and doesn't seem to work.

Adding CSS screentshot

enter image description here

Imnotapotato
  • 5,308
  • 13
  • 80
  • 147

2 Answers2

7

UPDATE

With base on the github files, you are targeting the unchanged css on the php file. That will not be parsed by webpack, remove it.

<link rel="stylesheet" type="text/css" href="css/main.css">

If you run npm run build your entry point is the JS file, this will be the parsed JS file though webpack that will generate all your files and will need to be included.

Then you are using mini css extract plugin to copy your css to somewhere, you need to load that file. By your current configuration it is saving one level up on a css folder:

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

Whatever this file outputs is what you should be loading not the original main.css, so by your current folder structure this file would be on one level UP path. Not in the public/css that you are probably expecting, if I am not mistaken if you use ./css/[name].css it should output to where you are expecting.

As a side note here, it seems you are using the same folder for the source and output. Try to move to separated folders just so you don't overwrite anything unwillingly.

Finally the fonts: The test of the loader needs to match your font

src: url("../webfonts/fa-brands-400.eot");

does not match the test:

test: /\.(png|gif|jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)/,

You probably want to make that last part optional

test: /\.(png|gif|jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)?/,

also your css-loader has url=false so the font resolver would never be invoked. Remove the url: false from your loader. Then is just a case of playing with the options of the file-loader, you can change the public-path to go to whatever you store your files and they would be replaced on the generated css and output path to copy them to the desired location.

So to summarise:

  • Check if you are importing the right css file, rename the file and see where it lands if you need assurance
  • If you want the url and the loader to be replaced , remove the url:false from css-loader and ensure the fonts files regex is matching them.
  • To avoid confusion store all the output on separated folder and check what lays where.

First answer:

If you are already using font-awesome and webpack I would suggest you use the font-awesome-loader.

That would be the easiest way to load them but a deeper explanation is that basically for every file extension webpack requires a loader to handle it. It will handle the file appropriately and put its contents somewhere.

So the steps to make webpack load the fontawesome fonts are:

  • Install the font awesome package in your project (or have the assets on some fixed place).
  • Load the font files using some loader like below

    module.exports = {
        module: {
            loaders: [
            // the file-loader will copy the file and fix the appropriate url
            {
                test: /\.(ttf|eot|svg|woff(2)?)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                loader: "file-loader",
                options: {
                name: "[name].[ext]",
                outputPath: "fonts/", 
                publicPath: "../fonts/"
                }
            }
            ]
        }
    };
    
  • Load the appropriate CSS/SCSS/LESS from font-awesome on your css.

So analysing your code on this part:

        // Forgot why I need this... 
        {
            test: /\.(png|gif|jpe|jpg|woff|woff2|eot|ttf|svg)(\?.*$|$)/,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 100000
                }
            }]
        }

You decided to load all these extensions through url-loader so they would become base64 URIs.

You loaded all font awesome css here

@import '../assets/icons/font-awesome/css/fontawesome-all.css';

If you check the CSS it is referencing the files by a given path and you choose the url-loader so the path will not match. If you change to the file-loader and make the options match the appropriate path it will copy the files there and you should be all set to go.

FabioCosta
  • 3,069
  • 6
  • 28
  • 50
  • I researched a little bit and decided to use file-loader instead (mainly because of caching). I do want to load the fonts through CSS (`@import '../assets/icons/font-awesome/css/fontawesome-all.css';`) because I do have other css file that depend on loading things as such in the CSS files and cannot risk to not set it. I'm editing my post with what i currently have set via webpack.conf.js. It's not generating any new file copies of the fonts to the set directory - and not refering to them via URL when loading the page. What part am I missing? – Imnotapotato Oct 09 '18 at 08:34
  • maybe the proper way to use this loader is to add it into a css "test" and not as a whole for itself? (since it loads fonts via css...?) – Imnotapotato Oct 09 '18 at 08:49
  • If you are using the CSS directly (Remember that font awesome have SCSS, less and other files as well) and it is expecting the file on a specific URL the trick is to make the webpack output the font files on the expected folder, check the output path and publich path options of file-loader. Another easy win is to just use the font awesome CDN. Anyway check if file-loader is finding the files, outputing on the expected path and that should do it. – FabioCosta Oct 09 '18 at 09:57
  • Yes i know, "the trick is to make the webpack output the font files on the expected folder".. it's strangely not outputting anything.. I added an other screenshot btw.. – Imnotapotato Oct 09 '18 at 10:00
  • So I don't know where you got this css but anyway it is expecting on ../webfonts/fa-brands-400.eot for example, so you need to make file-loader output to there or make the public path match this virtual location. Here is a nice article about [that](https://medium.com/@raviroshan.talk/webpack-understanding-the-publicpath-mystery-aeb96d9effb1) – FabioCosta Oct 09 '18 at 10:22
  • It might be a problem because I'm using more than one css file pointing on multiple font folders/files.. I"ll read it now.. The company that i work in purchased a theme that is structured like this and i'm trying to make everything more "workable". used to use gulp but I took webpack as a new experiment. – Imnotapotato Oct 09 '18 at 10:59
  • shouldn't `name: [path][name].[ext]` resolve all the problem, generate files in each folder the css is referencing and get generate a new css file which points to these folders? This is so confusing – Imnotapotato Oct 09 '18 at 11:01
  • Basically the file-loader publicPath can match a path that does not exist (like the webFonts) and you can store the files itself somewhere else (the outputPath). Webpack css-loader will try to read the file, that by its extension would match the file-loader that would take care of checking the path and outputing it, If you chose to go with url-loader for instance, css-loader would invoke url-loader and replace the path with the ecoded URI. – FabioCosta Oct 09 '18 at 11:08
  • I'm still on it.. I created a repo with the file system, if you have any spare time for helping me figure out why it's not creating new font files to the `public/fonts/` directory and referencing them within the css file https://github.com/hAtul89/no-files – Imnotapotato Oct 10 '18 at 15:15
  • 1
    I can check that out tomorrow night, will see what's what. Till there – FabioCosta Oct 11 '18 at 09:18
  • what do you mean by that :o ? – Imnotapotato Oct 15 '18 at 07:04
  • That the points that I put on the edited answer are what is wrong with your current code – FabioCosta Oct 15 '18 at 07:09
  • 1
    @FabioCosta Why don't you have more upvotes? This is clearly the answer! What a champ. I was missing `publicPath` and that really messed my relative paths. Works like a charm! – MCFreddie777 Sep 10 '20 at 20:31
  • As of `webpack@5.52.1` and `css-loader@6.2.0`, fonts files will be handle automatically by the `css-loader`: No need of any extra file loader. I'm pretty sure that's a new behavior and I cannot find in the documentation how to control it but it works anyway. – G. Vallereau Sep 16 '21 at 17:43
0

I've just posted a detailed answer on another similar question like this. That could help you and also includes another possible solution with the new way of using FontAwesome5 with SVG+JS. With that there's no need for font files, Webpack loaders, etc... Just a few extra lines in your JavaScript code.

(I hope posting an answer like this is not against the rules. That another is a long writing, I don't want to copy-paste it. Should I? I don't think the duplicate flag could be used here...)

szegheo
  • 4,175
  • 4
  • 31
  • 35