Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vite application manager #45257

Closed
wants to merge 11 commits into from
Closed

Vite application manager #45257

wants to merge 11 commits into from

Conversation

daniser
Copy link
Contributor

@daniser daniser commented Dec 10, 2022

Currently Vite helper is a singleton which mean that it may hold only one configuration at a time, allowing developer to customize only entry points and build directory per __invoke call. This may cause problems in multi-app configurations.

Example
In our project we use many SPAs within mainly legacy codebase. We manage our apps using Yarn workspaces. Axios, Echo and Ziggy configurations are shared between apps and stored in resources/js/bootstrap.js (resources/js/app.js entry point), as suggested by Laravel. This bootstrap code is injected into main page layout and accessible throughout the entire project and all its sub-apps.
The problem arose when it became necessary to debug specific app using yarn dev (vite) command to serve its assets. Vite can't serve multiple applications at once, so we need to use separate hot files for each application. But Laravel's Vite helper can only hold one configuration... so here we are.

Reasoning

  • allow Vite helper to manage multiple app configurations;
  • take app configuration out of Blade templates and store it in centralized location;
  • add support for app factory so new apps can be added without need to configure them separately;
  • make it possible to configure default "app" for each environment separately via VITE_APP environment variable.

Backward compatibility
Vite facade, Vite helper injection and @vite Blade directive should work as before, but instead of resolving singleton from application's container, they will point to "default" entry from the ViteManager.
Vite contract has been added to allow ViteManager to implement it.

Configuration
There are 3 ways to configure Vite "apps":

  1. config/vite.php file (should probably be added into laravel/laravel repo?):
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Vite App
    |--------------------------------------------------------------------------
    */

    'app' => env('VITE_APP', 'default'),

    'apps' => [

        'default' => [
            'entry_points' => ['vite/legacy-polyfills-legacy', 'resources/js/app-legacy.js', 'resources/js/app.js'],
            'build_directory' => 'build',
        ],

        'myapp' => [
            'entry_points' => ['vite/legacy-polyfills-legacy', 'src/app-legacy.js', 'src/app.js'],
            'build_directory' => 'build/packages/myapp',
            'hot_file' => 'build/packages/myapp/hot',
        ],

    ],

];
  1. Service provider's boot method:
<?php

namespace App\Providers;

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

class ViteServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Vite::withEntryPoints(['vite/legacy-polyfills-legacy', 'resources/js/app-legacy.js', 'resources/js/app.js'])
            ->useBuildDirectory('build');

        Vite::app('myapp')
            ->withEntryPoints(['vite/legacy-polyfills-legacy', 'src/app-legacy.js', 'src/app.js'])
            ->useBuildDirectory('build/packages/myapp')
            ->useHotFile(public_path('build/packages/myapp/hot'))
            ->useScriptTagAttributes(function (string $src) {
                return Str::contains($src, '-legacy') ? ['type' => 'text/javascript', 'nomodule'] : [];
            })
            ->usePreloadTagAttributes(function (string $src) {
                return Str::contains($src, '-legacy') ? ['rel' => 'dummy'] : [];
            });
    }
}
  1. App factory:
<?php

namespace App\Providers;

use Illuminate\Contracts\Foundation\Vite as ViteContract;
use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

class ViteServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Vite::useAppFactory(fn (ViteContract $vite, string $app, array $config) => $vite
            ->withEntryPoints(['vite/legacy-polyfills-legacy', 'src/app-legacy.js', 'src/app.js'])
            ->useBuildDirectory("build/packages/$app")
            ->useHotFile(public_path("build/packages/$app/hot"))
            ->useScriptTagAttributes(function (string $src) {
                return Str::contains($src, '-legacy') ? ['type' => 'text/javascript', 'nomodule'] : [];
            })
            ->usePreloadTagAttributes(function (string $src) {
                return Str::contains($src, '-legacy') ? ['rel' => 'dummy'] : [];
            })
            // amend/fall back to config entry for the app (if exists) from the "config/vite.php" file
            ->configure($config)
        );
    }
}

Usage
@viteApp directive should be used in Blade templates to enjoy the power of multiappness to its fullest:

<!-- Shared bootstrap code -->
@viteApp

<!-- Example app -->
@viteApp('myapp')

Testing
I should probably add some tests for this feature, but I'm not good at it, so any help will be appreciated.
For now, I'll mark this PR as draft and try to write some tests.

@daniser daniser changed the title Feature/vite multi app Vite app manager Dec 10, 2022
@daniser daniser changed the title Vite app manager Vite application manager Dec 10, 2022
@driesvints driesvints requested review from timacdonald and jessarcher and removed request for timacdonald December 12, 2022 08:12
@daniser daniser marked this pull request as ready for review December 12, 2022 11:07
@taylorotwell
Copy link
Member

I wonder if this could be written as a package for your own usage purposes? It feels a bit like a niche case to add more complexity to our own code base and maintenance burden for.

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.

2 participants