Skip to content

[9.x] Vite#42785

Merged
taylorotwell merged 14 commits into
9.xfrom
vite
Jun 22, 2022
Merged

[9.x] Vite#42785
taylorotwell merged 14 commits into
9.xfrom
vite

Conversation

@jessarcher

@jessarcher jessarcher commented Jun 13, 2022

Copy link
Copy Markdown
Member

⚡ Vite is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production.

This PR introduces a @vite Blade directive to the framework that will be used in combination with the official Laravel Vite Plugin. This PR does not remove or deprecate the existing mix() helper. There are also PRs to laravel/laravel as well as laravel/breeze and laravel/jetstream that will make this the default for new Laravel applications. There is also, of course, documentation.

The Laravel side of the implementation is similar to Mix and should feel quite familiar.

The main difference is that instead of using the mix() helper, you would now use the @vite Blade directive. The reason for the directive is that Laravel needs to output complete <script> and <link rel="stylesheet"> tags, rather than just a path as with Mix.

For the best experience with a Blade app, you will want to specify both CSS and JavaScript entry points, which would look something like this in your Blade layout template:

- <link rel="stylesheet" href="{{ mix('css/app.css') }}">
- <script src="{{ mix('js/app.js') }}" defer></script>
+ @vite(['resources/css/app.css', 'resources/js/app.js'])

In development mode, this will also load the Vite client, which will automatically reload your JavaScript and CSS when you make changes. In production mode, it will automatically load the versioned assets for you.

For the best experience with a SPA (e.g. Inertia), you will want to import your CSS via your JavaScript entry point(s) and then only specify the JavaScript entry point to the @vite directive, which would look something like this:

  import './bootstrap.js';
+ import '../css/app.css';
- <link rel="stylesheet" href="{{ mix('css/app.css') }}">
- <script src="{{ mix('js/app.js') }}" defer></script>
+ @vite('resources/js/app.js')

When importing your CSS via JavaScript, Vite will automatically inject a <style> tag in development mode that it will keep up to date. When building for production, a compiled app.{version}.css file will be generated that the @vite directive will automatically load via a <link rel="stylesheet"> tag.

When using React, you will also need to place a @viteReactRefresh directive above your @vite directive that will inject Vite's React Refresh Runtime.

Everything will be configured for you with Laravel's updated starter kits.

As with Mix, The @vite directive will automatically detect when the Vite development server is running, otherwise, it will use the compiled and versioned assets from the manifest.

The Laravel plugin also handles Vite SSR builds and works well with tools like Sail and Vapor.

The documentation will include full instructions, examples, and advanced configuration.

This implementation requires a specific Vite configuration that the official Laravel Vite Plugin handles for you. For existing applications, there are also some required changes documented in the upgrade guide.

@bram-pkg

Copy link
Copy Markdown
Contributor

Will the Vite manifest SRI plugin be supported by this? That would be nice.

@jessarcher

Copy link
Copy Markdown
Member Author

Will the Vite manifest SRI plugin be supported by this? That would be nice.

It's on my list of things to look at post-launch and should be easy to implement 👍

Comment thread src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php
@bjhijmans

Copy link
Copy Markdown
- <link rel="stylesheet" href="{{ mix('css/app.css') }}">
- <script src="{{ mix('js/app.js') }}" defer></script>
+ @vite(['resources/css/app.css', 'resources/js/app.js'])

How would you add nonces to this vite call?

Comment thread tests/Foundation/FoundationViteTest.php Outdated
Co-authored-by: Lucas Michot <513603+lucasmichot@users.noreply.github.com>
@jessarcher
jessarcher marked this pull request as ready for review June 22, 2022 02:17
@jessarcher
jessarcher marked this pull request as draft June 22, 2022 02:17
@taylorotwell
taylorotwell marked this pull request as ready for review June 22, 2022 18:07
@taylorotwell
taylorotwell merged commit baf526e into 9.x Jun 22, 2022
@taylorotwell
taylorotwell deleted the vite branch June 22, 2022 18:07
turanjanin pushed a commit to turanjanin/laravel-framework that referenced this pull request Jun 23, 2022
* Add Vite helpers

* Remove vite helper

* Add @viteReactRefresh directive

* Add docblock

* Remove unused import

* Automatically clean up after tests

* Use ASSET_URL when resolving assets

* support default entrypoint

* update docblock

* Linting

* Support CSS entry points

* Remove default entry points

Blade apps and SPAs have different entry point requirements so there is
no sensible default.

* Fix test namespace

Co-authored-by: Lucas Michot <513603+lucasmichot@users.noreply.github.com>

* Add missing import

Co-authored-by: Tim MacDonald <hello@timacdonald.me>
Co-authored-by: Lucas Michot <513603+lucasmichot@users.noreply.github.com>
@tabuna

tabuna commented Jun 27, 2022

Copy link
Copy Markdown
Contributor

Hi @jessarcher

The main difference is that instead of using the mix() helper, you would now use the @Vite Blade directive. The reason for the directive is that Laravel needs to output complete <script> and tags, rather than just a path as with Mix.

Is there a way to output the path like it was with mix() because it is often required to specify additional things like for link tags:

<link rel="stylesheet" type="text/css" href="{{  mix('/css/light.css') }}"
 media="(prefers-color-scheme: light)">

For script:

<script src="{{  mix('/app/part/landing.js') }}" 
data-turbo-eval="false" data-turbolinks-eval="false"></script>

@timacdonald

Copy link
Copy Markdown
Member

@tabuna there isn't a way to do this currently with Vite, however we are considering enabling the specification of arbitrary attributes on entry point tags.

@mikemand

Copy link
Copy Markdown

@tabuna there isn't a way to do this currently with Vite, however we are considering enabling the specification of arbitrary attributes on entry point tags.

I was thinking about this also, about how I could add a CSP nonce to the script tag. What about a blade template that can be overridden? That way, instead of calling makeTag/makeScriptTag/makeStylesheetTag, the URLs are pushed to the collection instead and then passed to a blade template.

@nlvedwin

Copy link
Copy Markdown

after installing a fresh app
image
image

@timacdonald

Copy link
Copy Markdown
Member

@nlvedwin it looks like you are visiting the Vite dev server. You still need to utilise php artisan serve, Valet or similar.

For example if you are using Valet on your system, you should visit laravel-vite.test instead.

@aurukas

aurukas commented Jun 29, 2022

Copy link
Copy Markdown

facing the same issue as @nlvedwin. I use Sail with localhost already being exposed and accessible.
npm run dev loads dev server with localhost:3000 which in theory should load the page with hmr? Unfortunately it throws 404 instead.

@bram-pkg

bram-pkg commented Jun 29, 2022 via email

Copy link
Copy Markdown
Contributor

@aurukas

aurukas commented Jun 29, 2022

Copy link
Copy Markdown

No, you load the normal artisan serve URL, @.` will take of making it able to hot reload from the development server.

On Wed, 29 Jun 2022 at 17:28, Aurelijus Ušeckas @.
> wrote: facing the same issue as @nlvedwin https://github.com/nlvedwin. I use Sail with localhost already being exposed and accessible. npm run dev loads dev server with localhost:3000 which in theory should load the page with hmr? Unfortunately it throws 404 instead. — Reply to this email directly, view it on GitHub <#42785 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADFQHI5AHHCQTHKLIJRJACDVRRTRBANCNFSM5YTKQLKA . You are receiving this because you commented.Message ID: @.***>

ahh OK. Got it now. Thanks! 🙏

@nlvedwin

nlvedwin commented Jun 29, 2022

Copy link
Copy Markdown

@nlvedwin it looks like you are visiting the Vite dev server. You still need to utilise php artisan serve, Valet or similar.

For example if you are using Valet on your system, you should visit laravel-vite.test instead.

@nlvedwin it looks like you are visiting the Vite dev server. You still need to utilise php artisan serve, Valet or similar.

For example if you are using Valet on your system, you should visit laravel-vite.test instead.

got it now, it feels like magic 🧙, thanks @timacdonald !

@mikemand

mikemand commented Jun 30, 2022

Copy link
Copy Markdown

For others who are having a 404 when trying to check the assets (http://localhost:3000/build/css/app.css), run vite build first to see if there are any errors compiling. I moved from SASS to PostCSS at the same time as upgrading to Vite and had a few errors that PostCSS couldn't deal with (commented out @apply, for one).

It would be great if the dev server didn't swallow compile errors, but I thnk that's something to bring up to Vite.

@chrispage1

Copy link
Copy Markdown
Contributor

Hi all, referred here by @tabuna. For those of you who are looking for a direct replacement of the mix() helper, please see my Vite helper package on Packagist - https://packagist.org/packages/motomedialab/laravel-vite-helper

@timacdonald

Copy link
Copy Markdown
Member

Hey folks,

A lot of questions in this thread about adding a CSP nonce, Subresource integrity, or arbitrary attributes (turoblinks, media query stuff, etc) to the Vite generated tags.

I've just opened a PR to support all of those use-cases.

You can check it out here: #43442

Any feedback would be appreciated.

@bram-pkg

Copy link
Copy Markdown
Contributor

Awesome stuff @timacdonald, thank you so much!

@kitro

kitro commented Aug 2, 2022

Copy link
Copy Markdown

Great work @jessarcher 👍 ,
I think it will be nice if we can also use @vitejs/plugin-legacy to inject nomdule script tag. In some cases like using the iPhone with iOS 13 that not support modular bundles <script type="module">.
Thank you,

@kitro

kitro commented Aug 2, 2022

Copy link
Copy Markdown

I think it will be nice if we can also use @vitejs/plugin-legacy to inject nomdule script tag. In some cases like using the iPhone with iOS 13 that not support modular bundles <script type="module">.

After #43442 I get a workaround to inject legacy generated files

        Vite::useScriptTagAttributes(function (string $src, string $url, array|null $chunk, array|null $manifest) {
            if (in_array(
                $src,
                [
                    'vite/legacy-polyfills',
                    'app/resources/js/app/app-legacy.js',
                    'app/resources/sass/app/app-legacy.scss',
                ]
            )) {
                return [
                    'type' => 'text/javascript',
                    'nomodule',
                ];
            }

            return [];
        });

@FreekVR

FreekVR commented Feb 15, 2023

Copy link
Copy Markdown

Late comment, but while the above works (thanks!) it is still loading the legacy script via preloads for modern browsers

eg.
<link rel="modulepreload" href="/build/assets/app-legacy-d2b2bf94.js" />

So far I've been unable to find a workaround for this

@FreekVR

FreekVR commented Feb 16, 2023

Copy link
Copy Markdown

I was able to find an ondocumented callback for the preloads, in which returning false causes no preloads to be included.
By default, a normal or module preload will cause the legacy bundle(s) to be downloaded even in modern browsers.

The following worked for us in a ViteServiceProvider we added to the project.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;

class ViteServiceProvider extends ServiceProvider
{
    protected const NONMODULE_FILES = [
        'vite/legacy-polyfills-legacy',
        'resources/frontend/app/app-legacy.js',
    ];

    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Vite::usePreloadTagAttributes(function ($src, $url, $chunk, $manifest) {
            // Don't preload non-module files at all
            if (in_array($src, self::NONMODULE_FILES)) {
                return false;
            }
            // Use the default attributes for the rest
            return [];
        });
        Vite::useScriptTagAttributes(function (string $src, string $url, array|null $chunk, array|null $manifest) {
            if (in_array($src, self::NONMODULE_FILES)) {
                return [
                    'type' => 'text/javascript',
                    'nomodule',
                ];
            }
            return [];
        });
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.