Search code examples
npmwebpackfont-awesomewebpack-2

Packaging Font Awesome font files in a separate directory with Webpack 2


I am attempting to use Webpack 2 and Font Awesome in a Cordova application. The Cordova application generates a file structure like this:

app
config.xml
hooks
node_modules
package.json
platforms
plugins
webpack.config.js
www

(This naturally includes my NPM files and webpack configuration, for context. app is where I squirrel away the raw JSX source code).

The structure under www (which is where the compiled out web application should go), looks like this:

css
fonts
img
index.html
js

I created the fonts directory as a target for the webpack configuration. The problem that I am running into is that I can either have the fonts copied to some useful location under the root or I can have the filtered CSS correct, but not both.

For example, if I use an example like the one below:

        {
            test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
            loader: "file-loader?name=[name].[ext]&outputPath=www/fonts/&publicPath=/fonts/"
        }

The files get placed under www, but the compiled source comes out strange.

@font-face {
  font-family: 'FontAwesome';
  src: url(/fonts/www/fonts/fontawesome-webfont.eot);
  src: url(/fonts/www/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'), url(/fonts/www/fonts/fontawesome-webfont.woff2) format('woff2'), url(/fonts/www/fonts/fontawesome-webfont.woff) format('woff'), url(/fonts/www/fonts/fontawesome-webfont.ttf) format('truetype'), url(/fonts/www/fonts/fontawesome-webfont.svg#fontawesomeregular) format('svg');
  font-weight: normal;
  font-style: normal;
}

I have yet to be able to find a happy medium with the file loader when trying to place fonts under www. The recommendations in How to configure font file output directory from font-awesome-webpack in webpack? and Can't load font-awesome with Webpack really have not gotten me anywhere.


Solution

  • The outputPath option in the file-loader is relative to your output directory and therefore will be included in the path. And publicPath just adds the given path to the beginning of the used paths, it basically means that the output directory will be located somewhere else on the server. This leaves the original path unchanged, since the structure within the directory must be unchanged.

    To make it work as you described, you need to change how you handle the output directory. As you've already mentioned your output directory is www, that's where webpack should put everything. Therefore it makes sense to configure output.path to www.

    output: {
      path: path.resolve(__dirname, 'www'),
      // Other output options
    }
    

    With this you don't need to specify www in every output name/path. For instance you might have done something along these lines: filename: 'www/bundle.js, this now becomes just filename: 'bundle.js'. Even though the result is the same, the concept behind it is different, because you just tell webpack where to put the output files, but only the filename itself is relevant for any processing, whereas the output directory is irrelevant.

    Now you have to change the outputPath in the file-loader to fonts/ and without the publicPath you would get the following URL:

    url(fonts/fontawesome-webfont.eot);
    

    That's a relative path, and you probably want to make it a server-relative path. The only thing that is missing is the leading /, therefore you'd set the publicPath to / and your rule becomes (using the nicer webpack 2 syntax instead of inlining the options, which makes it easier to read):

    {
      test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath: 'fonts/',
        publicPath: '/'
      }
    }
    

    You'll probably want other loaders, that include assets, to also respect the same public path, so instead of defining it per loader, you can set it in output.publicPath.

    output: {
      path: path.resolve(__dirname, 'www'),
      publicPath: '/',
      // Other output options
    }