From ed6c5e4a92061629cfc8e258b3064a4bc1e5d8cd Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 13 Aug 2024 11:04:45 +1000 Subject: [PATCH 1/6] Adds asset prefetching strategies --- src/Illuminate/Foundation/Vite.php | 155 +++++++- tests/Foundation/FoundationViteTest.php | 364 ++++++++++++++++++ .../fixtures/prefetching-manifest.json | 284 ++++++++++++++ 3 files changed, 802 insertions(+), 1 deletion(-) create mode 100644 tests/Foundation/fixtures/prefetching-manifest.json diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 46deeb8f0041..1d964d21406b 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Support\Collection; use Illuminate\Support\HtmlString; +use Illuminate\Support\Js; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; @@ -96,6 +97,20 @@ class Vite implements Htmlable */ protected static $manifests = []; + /** + * The prefetching strategy to use. + * + * @var null|'waterfall'|'aggressive' + */ + protected $prefetchStrategy = null; + + /** + * The number of assets to load concurrently when using the "waterfall" strategy. + * + * @var int + */ + protected $prefetchChunks = 3; + /** * Get the preloaded assets. * @@ -166,6 +181,24 @@ public function useManifestFilename($filename) return $this; } + /** + * Set the prefetching strategy. + * + * @param 'waterfall'|'aggressive'|null $strategy + * @param array $config + * @return $this + */ + public function usePrefetchStrategy($strategy, $config) + { + $this->prefetchStrategy = $strategy; + + if ($strategy === 'waterfall') { + $this->prefetchChunks = $config['chunks'] ?? $this->prefetchChunks; + } + + return $this; + } + /** * Resolve asset paths using the provided resolver. * @@ -363,7 +396,127 @@ public function __invoke($entrypoints, $buildDirectory = null) ->sortByDesc(fn ($args) => $this->isCssPath($args[1])) ->map(fn ($args) => $this->makePreloadTagForChunk(...$args)); - return new HtmlString($preloads->join('').$stylesheets->join('').$scripts->join('')); + $base = $preloads->join('').$stylesheets->join('').$scripts->join(''); + + if ($this->prefetchStrategy === null || $this->isRunningHot()) { + return new HtmlString($base); + } + + $discoveredImports = []; + + return collect($entrypoints) + ->flatMap(fn ($entrypoint) => collect($manifest[$entrypoint]['dynamicImports'] ?? []) + ->map(fn ($import) => $manifest[$import]) + ->filter(fn ($chunk) => str_ends_with($chunk['file'], '.js') || str_ends_with($chunk['file'], '.css')) + ->flatMap($f = function ($chunk) use (&$f, $manifest, &$discoveredImports) { + return collect([...$chunk['imports'] ?? [], ...$chunk['dynamicImports'] ?? []]) + ->reject(function ($import) use (&$discoveredImports) { + if (isset($discoveredImports[$import])) { + return true; + } + + return ! $discoveredImports[$import] = true; + }) + ->reduce( + fn ($chunks, $import) => $chunks->merge( + $f($manifest[$import]) + ), collect([$chunk])) + ->merge(collect($chunk['css'] ?? [])->map( + fn ($css) => collect($manifest)->first(fn ($chunk) => $chunk['file'] === $css) ?? [ + 'file' => $css, + ], + )); + }) + ->map(function ($chunk) use ($buildDirectory, $manifest) { + return collect([ + ...$this->resolvePreloadTagAttributes( + $chunk['src'] ?? null, + $url = $this->assetPath("{$buildDirectory}/{$chunk['file']}"), + $chunk, + $manifest, + ), + 'rel' => 'prefetch', + 'href' => $url, + ])->reject( + fn ($value) => in_array($value, [null, false], true) + )->mapWithKeys(fn ($value, $key) => [ + $key = (is_int($key) ? $value : $key) => $value === true ? $key : $value, + ])->all(); + }) + ->reject(fn ($attributes) => isset($this->preloadedAssets[$attributes['href']]))) + ->unique('href') + ->values() + ->pipe(fn ($assets) => with(Js::from($assets), fn ($assets) => match ($this->prefetchStrategy) { + 'waterfall' => new HtmlString($base.<< + window.addEventListener('load', () => window.setTimeout(() => { + const linkTemplate = document.createElement('link') + linkTemplate.rel = 'prefetch' + + const makeLink = (asset) => { + const link = linkTemplate.cloneNode() + + Object.keys(asset).forEach((attribute) => { + link.setAttribute(attribute, asset[attribute]) + }) + + return link + } + + const loadNext = (assets, count) => window.setTimeout(() => { + if (count > assets.length) { + count = assets.length + + if (count === 0) { + return + } + } + + const fragment = new DocumentFragment + + while (count > 0) { + const link = makeLink(assets.shift()) + fragment.append(link) + count-- + + if (assets.length) { + link.onload = () => loadNext(assets, 1) + link.error = () => loadNext(assets, 1) + } + } + + document.head.append(fragment) + }) + + loadNext({$assets}, {$this->prefetchChunks}) + })) + + HTML), + 'aggressive' => new HtmlString($base.<< + window.addEventListener('load', () => window.setTimeout(() => { + const linkTemplate = document.createElement('link') + linkTemplate.rel = 'prefetch' + + const makeLink = (asset) => { + const link = linkTemplate.cloneNode() + + Object.keys(asset).forEach((attribute) => { + link.setAttribute(attribute, asset[attribute]) + }) + + return link + } + + const fragment = new DocumentFragment + {$assets}.forEach((asset) => fragment.append(makeLink(asset))) + document.head.append(fragment) + })) + + HTML), + })); } /** diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 950fc2c21495..d0ccacee3d71 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -6,6 +6,7 @@ use Illuminate\Foundation\ViteException; use Illuminate\Foundation\ViteManifestNotFoundException; use Illuminate\Support\Facades\Vite as ViteFacade; +use Illuminate\Support\Js; use Illuminate\Support\Str; use Orchestra\Testbench\TestCase; @@ -1297,6 +1298,369 @@ protected function makeViteManifest($contents = null, $path = 'build') file_put_contents(public_path("{$path}/manifest.json"), $manifest); } + public function testItCanPrefetchEntrypoint() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ]); + $this->assertSame(<< + + HTML, $html); + + $this->cleanViteManifest($buildDir); + } + + public function testItHandlesSpecifyingPageWithAppJs() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js', 'resources/js/Pages/Auth/Login.vue'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ]); + $this->assertStringContainsString(<<cleanViteManifest($buildDir); + } + + public function testItCanSpecifyWaterfallChunks() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall', ['chunks' => 10])->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ]); + $this->assertStringContainsString(<<cleanViteManifest($buildDir); + } + + public function testItCanPrefetchAggressively() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('aggressive')->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ]); + + $this->assertSame(<< + + HTML, $html); + + $this->cleanViteManifest($buildDir); + } + + public function testAddsAttributesToPrefetchTags() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) tap(ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall'))->useCspNonce('abc123')->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js", "nonce" => "abc123"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js", "nonce" => "abc123"], + ]); + $this->assertStringContainsString(<<cleanViteManifest($buildDir); + } + + public function testItNormalisesAttributes() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) tap(ViteFacade::withEntryPoints(['resources/js/app.js']))->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->usePreloadTagAttributes([ + 'key' => 'value', + 'key-only', + 'true-value' => true, + 'false-value' => false, + 'null-value' => null, + ])->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ]); + + $this->assertStringContainsString(<<cleanViteManifest($buildDir); + } + + public function testItPrefetchesCss() + { + $manifest = json_decode(file_get_contents(__DIR__.'/fixtures/prefetching-manifest.json')); + $buildDir = Str::random(); + $this->makeViteManifest($manifest, $buildDir); + app()->usePublicPath(__DIR__); + + $html = (string) ViteFacade::withEntryPoints(['resources/js/admin.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->toHtml(); + + $expectedAssets = Js::from([ + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/admin-runtime-import-CRvLQy6v.js"], + ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/admin-runtime-import-import-DKMIaPXC.js"], + ["rel" => "prefetch", "as" => "style", "href" => "https://example.com/{$buildDir}/assets/admin-runtime-import-BlmN0T4U.css"], + ]); + $this->assertSame(<< + + HTML, $html); + + $this->cleanViteManifest($buildDir); + } + protected function cleanViteManifest($path = 'build') { if (file_exists(public_path("{$path}/manifest.json"))) { diff --git a/tests/Foundation/fixtures/prefetching-manifest.json b/tests/Foundation/fixtures/prefetching-manifest.json new file mode 100644 index 000000000000..564ee66a4885 --- /dev/null +++ b/tests/Foundation/fixtures/prefetching-manifest.json @@ -0,0 +1,284 @@ +{ + "_ApplicationLogo-BhIZH06z.js": { + "file": "assets/ApplicationLogo-BhIZH06z.js", + "name": "ApplicationLogo", + "imports": [ + "__plugin-vue_export-helper-DlAUqK2U.js", + "_index-BSdK3M0e.js" + ] + }, + "_AuthenticatedLayout-DfWF52N1.js": { + "file": "assets/AuthenticatedLayout-DfWF52N1.js", + "name": "AuthenticatedLayout", + "imports": [ + "_ApplicationLogo-BhIZH06z.js", + "_index-BSdK3M0e.js" + ] + }, + "_GuestLayout-BY3LC-73.js": { + "file": "assets/GuestLayout-BY3LC-73.js", + "name": "GuestLayout", + "imports": [ + "_ApplicationLogo-BhIZH06z.js", + "_index-BSdK3M0e.js" + ] + }, + "_PrimaryButton-DuXwr-9M.js": { + "file": "assets/PrimaryButton-DuXwr-9M.js", + "name": "PrimaryButton", + "imports": [ + "__plugin-vue_export-helper-DlAUqK2U.js", + "_index-BSdK3M0e.js" + ] + }, + "_TextInput-C8CCB_U_.js": { + "file": "assets/TextInput-C8CCB_U_.js", + "name": "TextInput", + "imports": [ + "_index-BSdK3M0e.js" + ] + }, + "__plugin-vue_export-helper-DlAUqK2U.js": { + "file": "assets/_plugin-vue_export-helper-DlAUqK2U.js", + "name": "_plugin-vue_export-helper" + }, + "_index-!~{00f}~.js": { + "file": "assets/index-B3s1tYeC.css", + "src": "_index-!~{00f}~.js" + }, + "_index-BSdK3M0e.js": { + "file": "assets/index-BSdK3M0e.js", + "name": "index", + "css": [ + "assets/index-B3s1tYeC.css" + ] + }, + "resources/js/Pages/Auth/ConfirmPassword.vue": { + "file": "assets/ConfirmPassword-CDwcgU8E.js", + "name": "ConfirmPassword", + "src": "resources/js/Pages/Auth/ConfirmPassword.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_GuestLayout-BY3LC-73.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Auth/ForgotPassword.vue": { + "file": "assets/ForgotPassword-B0WWE0BO.js", + "name": "ForgotPassword", + "src": "resources/js/Pages/Auth/ForgotPassword.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_GuestLayout-BY3LC-73.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Auth/Login.vue": { + "file": "assets/Login-DAFSdGSW.js", + "name": "Login", + "src": "resources/js/Pages/Auth/Login.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_GuestLayout-BY3LC-73.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Auth/Register.vue": { + "file": "assets/Register-CfYQbTlA.js", + "name": "Register", + "src": "resources/js/Pages/Auth/Register.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_GuestLayout-BY3LC-73.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Auth/ResetPassword.vue": { + "file": "assets/ResetPassword-BNl7a4X1.js", + "name": "ResetPassword", + "src": "resources/js/Pages/Auth/ResetPassword.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_GuestLayout-BY3LC-73.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Auth/VerifyEmail.vue": { + "file": "assets/VerifyEmail-CyukB_SZ.js", + "name": "VerifyEmail", + "src": "resources/js/Pages/Auth/VerifyEmail.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_GuestLayout-BY3LC-73.js", + "_PrimaryButton-DuXwr-9M.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Dashboard.vue": { + "file": "assets/Dashboard-DM_LxQy2.js", + "name": "Dashboard", + "src": "resources/js/Pages/Dashboard.vue", + "isDynamicEntry": true, + "imports": [ + "_AuthenticatedLayout-DfWF52N1.js", + "_index-BSdK3M0e.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Profile/Edit.vue": { + "file": "assets/Edit-CYV2sXpe.js", + "name": "Edit", + "src": "resources/js/Pages/Profile/Edit.vue", + "isDynamicEntry": true, + "imports": [ + "_AuthenticatedLayout-DfWF52N1.js", + "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", + "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue", + "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue", + "_index-BSdK3M0e.js", + "_ApplicationLogo-BhIZH06z.js", + "__plugin-vue_export-helper-DlAUqK2U.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js" + ] + }, + "resources/js/Pages/Profile/Partials/DeleteUserForm.vue": { + "file": "assets/DeleteUserForm-B1oHFaVP.js", + "name": "DeleteUserForm", + "src": "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "__plugin-vue_export-helper-DlAUqK2U.js", + "_TextInput-C8CCB_U_.js" + ] + }, + "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue": { + "file": "assets/UpdatePasswordForm-CaeWqGla.js", + "name": "UpdatePasswordForm", + "src": "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue": { + "file": "assets/UpdateProfileInformationForm-CJwkYwQQ.js", + "name": "UpdateProfileInformationForm", + "src": "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js", + "_TextInput-C8CCB_U_.js", + "_PrimaryButton-DuXwr-9M.js", + "__plugin-vue_export-helper-DlAUqK2U.js" + ] + }, + "resources/js/Pages/Welcome.vue": { + "file": "assets/Welcome-D_7l79PQ.js", + "name": "Welcome", + "src": "resources/js/Pages/Welcome.vue", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js" + ] + }, + "resources/js/admin-runtime-import-import.js": { + "file": "assets/admin-runtime-import-import-DKMIaPXC.js", + "name": "admin-runtime-import-import", + "src": "resources/js/admin-runtime-import-import.js", + "isDynamicEntry": true + }, + "resources/js/admin-runtime-import.js": { + "file": "assets/admin-runtime-import-CRvLQy6v.js", + "name": "admin-runtime-import", + "src": "resources/js/admin-runtime-import.js", + "isDynamicEntry": true, + "imports": [ + "_index-BSdK3M0e.js" + ], + "dynamicImports": [ + "resources/js/admin-runtime-import-import.js" + ], + "css": [ + "assets/admin-runtime-import-BlmN0T4U.css" + ] + }, + "resources/js/admin.js": { + "file": "assets/admin-Sefg0Q45.js", + "name": "admin", + "src": "resources/js/admin.js", + "isEntry": true, + "imports": [ + "_index-BSdK3M0e.js" + ], + "dynamicImports": [ + "resources/js/Pages/Auth/ConfirmPassword.vue", + "resources/js/Pages/Auth/ForgotPassword.vue", + "resources/js/Pages/Auth/Login.vue", + "resources/js/Pages/Auth/Register.vue", + "resources/js/Pages/Auth/ResetPassword.vue", + "resources/js/Pages/Auth/VerifyEmail.vue", + "resources/js/Pages/Dashboard.vue", + "resources/js/Pages/Profile/Edit.vue", + "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", + "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue", + "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue", + "resources/js/Pages/Welcome.vue", + "resources/js/admin-runtime-import.js" + ], + "css": [ + "assets/admin-BctAalm_.css" + ] + }, + "resources/js/app.js": { + "file": "assets/app-lliD09ip.js", + "name": "app", + "src": "resources/js/app.js", + "isEntry": true, + "imports": [ + "_index-BSdK3M0e.js" + ], + "dynamicImports": [ + "resources/js/Pages/Auth/ConfirmPassword.vue", + "resources/js/Pages/Auth/ForgotPassword.vue", + "resources/js/Pages/Auth/Login.vue", + "resources/js/Pages/Auth/Register.vue", + "resources/js/Pages/Auth/ResetPassword.vue", + "resources/js/Pages/Auth/VerifyEmail.vue", + "resources/js/Pages/Dashboard.vue", + "resources/js/Pages/Profile/Edit.vue", + "resources/js/Pages/Profile/Partials/DeleteUserForm.vue", + "resources/js/Pages/Profile/Partials/UpdatePasswordForm.vue", + "resources/js/Pages/Profile/Partials/UpdateProfileInformationForm.vue", + "resources/js/Pages/Welcome.vue" + ] + } +} + From b6143556cc8b1e6bb1f97c7e8bee474631892ab4 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 15 Aug 2024 11:36:19 +1000 Subject: [PATCH 2/6] Formatting --- src/Illuminate/Foundation/Vite.php | 104 +++++----- tests/Foundation/FoundationViteTest.php | 246 ++++++++++++------------ 2 files changed, 175 insertions(+), 175 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 1d964d21406b..037cba0dfb1e 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -188,7 +188,7 @@ public function useManifestFilename($filename) * @param array $config * @return $this */ - public function usePrefetchStrategy($strategy, $config) + public function usePrefetchStrategy($strategy, $config = []) { $this->prefetchStrategy = $strategy; @@ -449,74 +449,74 @@ public function __invoke($entrypoints, $buildDirectory = null) ->pipe(fn ($assets) => with(Js::from($assets), fn ($assets) => match ($this->prefetchStrategy) { 'waterfall' => new HtmlString($base.<< - window.addEventListener('load', () => window.setTimeout(() => { - const linkTemplate = document.createElement('link') - linkTemplate.rel = 'prefetch' + - HTML), - 'aggressive' => new HtmlString($base.<<prefetchChunks}) + })) + + HTML), + 'aggressive' => new HtmlString($base.<< - window.addEventListener('load', () => window.setTimeout(() => { - const linkTemplate = document.createElement('link') - linkTemplate.rel = 'prefetch' + - HTML), - })); + const fragment = new DocumentFragment + {$assets}.forEach((asset) => fragment.append(makeLink(asset))) + document.head.append(fragment) + })) + + HTML), + })); } /** diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index d0ccacee3d71..5f3071ea197d 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -1308,24 +1308,24 @@ public function testItCanPrefetchEntrypoint() $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], ]); $this->assertSame(<< @@ -1387,18 +1387,18 @@ public function testItHandlesSpecifyingPageWithAppJs() $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js', 'resources/js/Pages/Auth/Login.vue'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], ]); $this->assertStringContainsString(<<useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall', ['chunks' => 10])->toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], ]); $this->assertStringContainsString(<<useBuildDirectory($buildDir)->usePrefetchStrategy('aggressive')->toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], ]); $this->assertSame(<<useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall'))->useCspNonce('abc123')->toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js", "nonce" => "abc123"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js", "nonce" => "abc123"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js", 'nonce' => 'abc123'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js", 'nonce' => 'abc123'], ]); $this->assertStringContainsString(<<toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js", "key" => "value", "key-only" => "key-only", "true-value" => "true-value"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js", 'key' => 'value', 'key-only' => 'key-only', 'true-value' => 'true-value'], ]); $this->assertStringContainsString(<<useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall')->toHtml(); $expectedAssets = Js::from([ - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/admin-runtime-import-CRvLQy6v.js"], - ["rel" => "prefetch", "href" => "https://example.com/{$buildDir}/assets/admin-runtime-import-import-DKMIaPXC.js"], - ["rel" => "prefetch", "as" => "style", "href" => "https://example.com/{$buildDir}/assets/admin-runtime-import-BlmN0T4U.css"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/GuestLayout-BY3LC-73.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/TextInput-C8CCB_U_.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/PrimaryButton-DuXwr-9M.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ApplicationLogo-BhIZH06z.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/_plugin-vue_export-helper-DlAUqK2U.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ForgotPassword-B0WWE0BO.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Login-DAFSdGSW.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Register-CfYQbTlA.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ResetPassword-BNl7a4X1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/VerifyEmail-CyukB_SZ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Dashboard-DM_LxQy2.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/AuthenticatedLayout-DfWF52N1.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Edit-CYV2sXpe.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/DeleteUserForm-B1oHFaVP.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdatePasswordForm-CaeWqGla.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/UpdateProfileInformationForm-CJwkYwQQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/Welcome-D_7l79PQ.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/admin-runtime-import-CRvLQy6v.js"], + ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/admin-runtime-import-import-DKMIaPXC.js"], + ['rel' => 'prefetch', 'as' => 'style', 'href' => "https://example.com/{$buildDir}/assets/admin-runtime-import-BlmN0T4U.css"], ]); $this->assertSame(<< From 60a12096e496e2159df79f8620faae7fab290583 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 16 Aug 2024 09:49:30 +1000 Subject: [PATCH 3/6] Rename config --- src/Illuminate/Foundation/Vite.php | 6 +++--- tests/Foundation/FoundationViteTest.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 037cba0dfb1e..5d6c42b50ccf 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -109,7 +109,7 @@ class Vite implements Htmlable * * @var int */ - protected $prefetchChunks = 3; + protected $prefetchConcurrently = 3; /** * Get the preloaded assets. @@ -193,7 +193,7 @@ public function usePrefetchStrategy($strategy, $config = []) $this->prefetchStrategy = $strategy; if ($strategy === 'waterfall') { - $this->prefetchChunks = $config['chunks'] ?? $this->prefetchChunks; + $this->prefetchConcurrently = $config['concurrently'] ?? $this->prefetchConcurrently; } return $this; @@ -489,7 +489,7 @@ public function __invoke($entrypoints, $buildDirectory = null) document.head.append(fragment) }) - loadNext({$assets}, {$this->prefetchChunks}) + loadNext({$assets}, {$this->prefetchConcurrently}) })) HTML), diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 5f3071ea197d..1e859438a02e 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -1414,7 +1414,7 @@ public function testItCanSpecifyWaterfallChunks() $this->makeViteManifest($manifest, $buildDir); app()->usePublicPath(__DIR__); - $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall', ['chunks' => 10])->toHtml(); + $html = (string) ViteFacade::withEntryPoints(['resources/js/app.js'])->useBuildDirectory($buildDir)->usePrefetchStrategy('waterfall', ['concurrently' => 10])->toHtml(); $expectedAssets = Js::from([ ['rel' => 'prefetch', 'href' => "https://example.com/{$buildDir}/assets/ConfirmPassword-CDwcgU8E.js"], From b755cd3a889e713b7933ce20f9cf2424fdf03c25 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Fri, 16 Aug 2024 10:02:04 +1000 Subject: [PATCH 4/6] Use low fetch priority --- src/Illuminate/Foundation/Vite.php | 11 +- tests/Foundation/FoundationViteTest.php | 261 ++++++++++++------------ 2 files changed, 129 insertions(+), 143 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 5d6c42b50ccf..3a52b9437cc3 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -436,6 +436,7 @@ public function __invoke($entrypoints, $buildDirectory = null) $manifest, ), 'rel' => 'prefetch', + 'fetchpriority' => 'low', 'href' => $url, ])->reject( fn ($value) => in_array($value, [null, false], true) @@ -451,11 +452,8 @@ public function __invoke($entrypoints, $buildDirectory = null)