laravel / vite-plugin

Laravel plugin for Vite.
MIT License
790 stars 148 forks source link

Vite Dev Server trying to serve assets referenced with an absolute path in CSS #302

Closed goldmerc closed 1 month ago

goldmerc commented 1 month ago

Vite Plugin Version

1.0.5

Laravel Version

11.20.0

Node Version

22.4.1

NPM Version

10.8.1

Operating System

macOS

OS Version

14.4.1

Web browser and version

Chrome 127.0.6533.89 (Official Build) (arm64)

Running in Sail?

No

Description

The laravel docs says: -

When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of caveats to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory.

If I use an absolute path in my css (for instance for a font which doesn't need versioning), and then run npm run dev, vite still tries to serve the font from the vite server...

http://127.0.0.1:5173/fonts/AvenirLTStd-Roman.woff

Which results in a 404.

Perhaps I'm missing something but if Vite is not including the item in the production build, why is it trying to serve it in dev? The behaviour I would expect would be to leave it using the absolute path, which is what happens in the production build.

Steps To Reproduce

repo to reproduce issue - https://github.com/goldmerc/vite-asset

jessarcher commented 1 month ago

The problem occurs because the CSS is being loaded from the Vite dev server during development (e.g. http://127.0.0.1:5173/resources/css/app.css). When the browser sees the absolute path in the CSS (i.e. /fonts/AvenirLTStd-Roman.woff), it loads from the same host as the CSS.

One option is to load the CSS via the JavaScript entrypoint instead of having it as a dedicated entrypoint. E.g:

// app.js
import '../css/app.css';
import './bootstrap';

(Don't forget to remove the CSS entrypoint from the Vite config and @vite helper)

This will cause Vite to inject the CSS into the page instead of linking to it during development, and so the browser will load the absolute path via the Laravel host instead. This can lead to a "flash of unstyled content" (FOUC) during development if your page is server-rendered (rather than client-rendered via something like Vue/React) because the JS code needs to initialize before the CSS can be injected.

If the FOUC drives you crazy (like it does for me), a workaround would be something like this in your vite.config.js file:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig(({ command }) => {
    const alias = command === 'serve' ? {
        '/fonts': '/public/fonts',
    }: {}

    return {
        resolve: {
            alias,
        },
        plugins: [
            laravel({
                input: ['resources/css/app.css', 'resources/js/app.js'],
                refresh: true,
            }),
        ],
    };
});

This will cause Vite to rewrite the absolute path from /fonts/AvenirLTStd-Roman.woff to /public/fonts/AvenirLTStd-Roman.woff during development only, causing the browser to load the font via http://127.0.0.1:5173/public/fonts/AvenirLTStd-Roman.woff, which will work because the Vite dev server will serve any asset from the root directory of your project.

There are potentially other options, but this is all I can think of now.

Personally, if I were using server-rendered content, I think I'd use a relative path and let Vite version it.

goldmerc commented 1 month ago

@jessarcher thanks! super helpful! I tried the alias method and that works great.

I had previously tried a relative path and that worked fine too. May I ask, what is the benefit of versioning fonts? I don't understand why you would need them in the build? The file size seems the same pre and post build, so there's no compression happening. Coding is just a hobby for me, so there's probably some reason I'm not aware of. Apologies for picking your brain!

It's great that you helped me but I wonder if the laravel docs need updating on this issue? They are quite clear that when "referencing assets in your application's HTML, CSS, or JS" using an absolute path Vite won't include them in the build. There's no warning that this will break your dev environment if you have absolute urls in your CSS. I spent quite a while searching on this issue and found several forum posts (laracasts, etc) where people were struggling with this same issue, both with fonts and images referenced from CSS. No one managed to answer those posts properly. You're the first person I've found who's written something explaining what's actually the problem and a solution. It took me a few hours to convince myself that I had actually migrated from Mix to Vite properly and the issue was not with my configuration.

Thanks again for your help and time.

jessarcher commented 1 month ago

Hey @goldmerc, I don't think there's much benefit to versioning fonts unless you're updating them and want to ensure the user has the latest version. However, I don't see any harm in versioning them, and it avoids this problem and keeps things consistent with other assets.

I agree the docs could be improved. I've submitted a small change at https://github.com/laravel/docs/pull/9824 to warn people about this issue. I'm hesitant to "officially" recommend the alias approach in the docs because it feels like more of a workaround, and I've only briefly tested it. Hopefully, determined folks will find this thread if they want to get it to work.