laravel-mix / laravel-mix

The power of webpack, distilled for the rest of us.
MIT License
5.26k stars 806 forks source link

Version hash in CSS does not match manifest and mix() output #2785

Closed greimers closed 3 years ago

greimers commented 3 years ago

Description:

I use mix with file versioning and I have custom fonts that I embed. These font assets get different hashes in the generated CSS files (where the fonts are defined) and in the HTML where I preload them using mix().

Steps To Reproduce:

My mixpack.mix.js looks like this:

mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [
        require('tailwindcss'),
    ]);

mix.copy('resources/img', 'public/img');
mix.copy('resources/fonts', 'public/fonts');

if (mix.inProduction()) {
    mix.version();
}

app.css imports fonts.css which has fonts defined like this:

@font-face {
    font-family: 'Rubik';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: local('Rubik Regular'),
    url('../fonts/rubik-v11-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
    url('../fonts/rubik-v11-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}

Now in my HTML header I preload the WOFF2 fonts like this:

<link rel="preload" href="{{ mix('fonts/rubik-v11-latin-regular.woff2') }}" as="font" type="font/woff2" crossorigin>

In the compiled CSS the font URL is

url(/fonts/rubik-v11-latin-regular.woff2?1d5a416e2889e382ab1af96fef09bd16)

but in the mix manifest file it is

"/fonts/rubik-v11-latin-regular.woff2": "/fonts/rubik-v11-latin-regular.woff2?id=e21357a42764554dd5e3"

and accordingly in the HTML the mix() function will also output the URL with the hash e21357a42764554dd5e3

So the hashes in the generated CSS differ from the hashes in the manifest and in the HTML. This breaks my preloading as the file will be loaded twice.


Now am I doing something wrong or is this a bug?

Also, I would be fine with excluding all WOFF files from the versioning as they won't change anyway. But the docs say I could either version everything or list each file separately. Is there some kind of exclusion list or a way to include entire folders? I don't want to put every single image as a paremeter to version().

I guess a workaround would be to put absolute URLs in the CSS file so they are not versioned. But I thought there must be a better solution.

JeffreyWay commented 3 years ago

I haven't heard of this before, but it seems like some kind of caching issue possibly. The mix() helper function is part of the Laravel source code. All it does is read your mix-manifest.json file and extract the hashed file path. So I have no clue why they'd be different in your case.

It's not an issue related to this particular repo, but play around with doing a hard refresh and checking if the hash changes.

greimers commented 3 years ago

I think I might not have explained clearly enough that this issue is not in the browser. It is directly in the generated files. There is no caching involved. When I delete my complete public folder and run npm run prod it will keep generating the CSS file with the wrong hash.

Also the result from mix() does match the hashes in the mix-manifest.json correctly. But the hashes that automatically get generated within the CSS do not match. So I think the issue is within the URL expansion when then CSS files are parsed. Is it possible that the hashing method used here differs from what is generated for the mix-manifest?

thecrypticace commented 3 years ago

How is your CSS file getting generated hashes appended to the assets?

greimers commented 3 years ago

That is done automatically by mix. I simply have relative paths in the CSS files. So when Mix parses the CSS it converts the paths to absolute URLs and as I have versions() appied, it also adds the hashes.

thecrypticace commented 3 years ago

I'm going to re-open for me to investigate this further. Ideally the hashes generated by mix would match those by webpack but I'm not yet sure of the feasibility of that. I've investigated this some previously and it appears to be rather difficult.

As this isn't strictly an error but a preloading issue this isn't super high priority but I'll see what I can figure out.

tbruckmaier commented 3 years ago

Discussion in #2025 is about the same issue, including some workarounds.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

taborskiy commented 5 months ago
$font-path: asset('fonts/your-font-file.woff');

@font-face {
    font-family: 'YourFontFamilyName';
    src: url('#{$font-path}') format('woff');
    /* Add more font formats if necessary */
}

/* Use the font-family in your styles */
body {
    font-family: 'YourFontFamilyName', sans-serif;
}

works for me