webpack-encore: Wrong path fonts

When I include semantic or bootstrap in my main.js I end up with a 404 not found error for the fonts

Apparently, the output path of fonts go to the root of my web server like this

http://localhost/build/fonts/icons.woff2

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Comments: 41 (12 by maintainers)

Commits related to this issue

Most upvoted comments

@heitorvrb Ah, thanks for the extra info! If you’re deployed under a subdirectory, that should be reflected in your setPublicPath:

// this is your *true* public path
.setPublicPath('/mySymfonyApp/build')
// this is now needed so that your manifest.json keys are still `build/foo.js`
// i.e. you won't need to change anything in your Symfony app
config.setManifestKeyPrefix('build')

You can see an example of this in the tests as well: https://github.com/symfony/webpack-encore/blob/db980b4018104b8f16a9f83111078ce86e7b76ad/test/functional.js#L180

Webpack must be aware of the true public path, because if you use “code splitting”, then webpack itself will make AJAX requests for assets (so it needs to know the real path). That’s why the config lives in webpack.config.js, instead of allowing your app (i.e. Symfony) to “fix” the subdirectory.

Let me know if that helps! I’ll open an issue on the docs about this.

@kevinpapst @weaverryan

I am in this scenario:

  • my symfony app is deployed in production under a subdirectory, e.g. www.myhost.com/apps/mySymfonyApp
  • the same app is under www.myhost.com/some/development/path/mySymfonyApp for development purposes and under www.myhost.com/some/demo/path/mySymfonyApp for demo purposes
  • if possibile, I don’t want to care in my config what is the path where the app is deployed

I have deleted these two lines of code in version 0.17.2 (they add a traling slash if it is not present), so that I can have a public path defined as '' (otherwise it is transformed to '/').

With this configuration in webpack.config.js

.setOutputPath('public/build/')
.setPublicPath('')
.setManifestKeyPrefix('build/')

all works correctly:

  • I can access assets from twig with

     {{ asset('build/app.js') }}
    
  • And if some scss file (like in Bootstrap) uses some other file (like glyphicons fonts) they are referenced properly. For example glyphicons-halflings-regular.eot is referenced as url(fonts/glyphicons-halflings-regular.f4769f9b.eot) in compiled css.

I am wondering: is this solution acceptable or are there some other consequences/scenarios which I can’t figure out (I’m new to encore/webpack and nodejs…)?

If it is acceptable, we could test if publicPath is not empty before adding the trailing slash.

@kevinpapst couldn’t you do simply

.setPublicPath('build')

or am I missing something?

What if my web root changes depending to the environment I’m deploying to? On my local development environment, the web root will be /my-nth-app/web/build whereas on the production server, it will just be /build. What would be the recommended way to make this dynamic, or at least, configurable?

@toklok Even without CI/CD I wouldn’t recommend commiting them. Building them locally before a deployment, or even directly on the server (which is not really a good thing either) are probably better solutions.

@stoccc and everyone who wants to test. you can change your package.json to

"@symfony/webpack-encore": "git://github.com/symfony/webpack-encore.git#0.17.2",

@Lyrkan commenting this error and passing relative path makes internal CSS files to import like

basedir/build/build/fonts/fontawesome.ttf

The build doubles since CSS file is placed inside the build and it calls relative build/fonts/.../

So we really need the possibility to rewrite the paths

I use sub directories for sites while in dev and it was a bit of a pain setting a publicPath. Especially when co-workers had different sub directory structures.

I wrote some code to automatically determine the public directory across all projects within the webpack config.

webpack.config.js

var Encore = require('@symfony/webpack-encore');

var publicPath = '/build';

if (!Encore.isProduction()) {
    require('dotenv').config({
        path: './.webpack.env',
    });

    if ('undefined' !== typeof process.env.ENCORE_PUBLIC_PATH) {
        publicPath = process.env.ENCORE_PUBLIC_PATH;
    } else {
        const guessFromPaths = [
            '/usr/local/var/www/htdocs',
            '/usr/local/var/www',
            process.env.HOME + '/Sites',
        ];

        for (var i = 0; i < guessFromPaths.length; i++) {
            var path = guessFromPaths[i];

            if (0 === __dirname.indexOf(path)) {
                path = __dirname.split(path);
                publicPath = path[1] + '/public/build';
                break;
            }
        }
    }
}

Encore
    // directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // public path used by the web server to access the output path
    .setPublicPath(publicPath)
    // only needed for CDN's or sub-directory deploy
    .setManifestKeyPrefix('build/')
...

You will need to install dotenv from yarn for this to work.

In the above code we are just giving it an array of potential web root directories to look for. This is a list that a few different co-workers are using.

As soon as the loop find a value that matches the __dirname variable, it will take that value and append /public/build to the value.

e.g. if your build files are located in /usr/local/var/www/htdocs/clientXYZ/project123/public/build it will make the public path /clientXYZ/project123/public/build

We also have a .encore.env.dist where you can set a value for ENCORE_PUBLIC_PATH

@weaverryan it seems that 0.17.2 has not yet been published to npmjs https://www.npmjs.com/package/@symfony/webpack-encore

@vctls Maybe you could use Encore.isProduction() to set the web root dynamically?

Encore.setPublicPath(Encore.isProduction() ? '/build' : '/my-nth-app/web/build');

Awesome! Then we should create a PR! I need to do some git blame to make sure I remember why that trailing slash is so important usually. However, based on why I’ve heard, we may be able to skip that trailing slash of the publicPath argument does not have the starting slash. Basically, you’d opt in to absolute paths or relative.

So yea - can someone create a PR? We have a great test suite in functional.js - it would be pretty easy to setup a seriously solid test to make sure the edge cases all work.

I tried many combinations, but missed that the slash is automatically added on an empty string. I can confirm that your solution works @stoccc. Thanks, good spotting! The manifest.json is generated correctly and the assets have a proper relative URL. Versioning works and I can use the JsonManifestVersionStrategy. @weaverryan any thoughts on an empty publicPath?