From 4bfcdd741d4ffd61ce85e8af089fd12090c5657f Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Fri, 6 May 2022 12:12:43 +1000 Subject: [PATCH 01/14] Add Vite helpers --- .../Concerns/InteractsWithContainer.php | 42 ++++++- src/Illuminate/Foundation/Vite.php | 103 +++++++++++++++++ src/Illuminate/Foundation/helpers.php | 17 +++ .../Compilers/Concerns/CompilesHelpers.php | 11 ++ tests/Foundation/FoundationHelpersTest.php | 108 ++++++++++++++++++ .../Concerns/InteractsWithContainerTest.php | 21 ++++ tests/View/Blade/BladeHelpersTest.php | 1 + 7 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 src/Illuminate/Foundation/Vite.php diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php index 6949f6f8c5da..41017d31bf3c 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php @@ -4,10 +4,18 @@ use Closure; use Illuminate\Foundation\Mix; +use Illuminate\Foundation\Vite; use Mockery; trait InteractsWithContainer { + /** + * The original Vite handler. + * + * @var \Illuminate\Foundation\Vite|null + */ + protected $originalVite; + /** * The original Laravel Mix handler. * @@ -90,6 +98,38 @@ protected function forgetMock($abstract) return $this; } + /** + * Register an empty handler for Vite in the container. + * + * @return $this + */ + protected function withoutVite() + { + if ($this->originalVite == null) { + $this->originalVite = app(Vite::class); + } + + $this->swap(Vite::class, function () { + return ''; + }); + + return $this; + } + + /** + * Restore Vite in the container. + * + * @return $this + */ + protected function withVite() + { + if ($this->originalVite) { + $this->app->instance(Vite::class, $this->originalVite); + } + + return $this; + } + /** * Register an empty handler for Laravel Mix in the container. * @@ -109,7 +149,7 @@ protected function withoutMix() } /** - * Register an empty handler for Laravel Mix in the container. + * Restore Laravel Mix in the container. * * @return $this */ diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php new file mode 100644 index 000000000000..599781515a1f --- /dev/null +++ b/src/Illuminate/Foundation/Vite.php @@ -0,0 +1,103 @@ +map(fn ($entrypoint) => $this->makeScriptTag("{$url}/{$entrypoint}")) + ->prepend($this->makeScriptTag("{$url}/@vite/client")) + ->join('') + ); + } + + $manifestPath = public_path($buildDirectory.'/manifest.json'); + + if (! isset($manifests[$manifestPath])) { + if (! is_file($manifestPath)) { + throw new Exception("Vite manifest not found at: {$manifestPath}"); + } + + $manifests[$manifestPath] = json_decode(file_get_contents($manifestPath), true); + } + + $manifest = $manifests[$manifestPath]; + + $scripts = collect(); + $stylesheets = collect(); + + foreach ($entrypoints as $entrypoint) { + if (! isset($manifest[$entrypoint])) { + throw new Exception("Unable to locate file in Vite manifest: {$entrypoint}."); + } + + $scripts->push( + $this->makeScriptTag("{$buildDirectory}/{$manifest[$entrypoint]['file']}") + ); + + if (isset($manifest[$entrypoint]['css'])) { + foreach ($manifest[$entrypoint]['css'] as $css) { + $stylesheets->push($this->makeStylesheetTag("{$buildDirectory}/{$css}")); + } + } + + if (isset($manifest[$entrypoint]['imports'])) { + foreach ($manifest[$entrypoint]['imports'] as $import) { + if (isset($manifest[$import]['css'])) { + foreach ($manifest[$import]['css'] as $css) { + $stylesheets->push($this->makeStylesheetTag("{$buildDirectory}/{$css}")); + } + } + } + } + } + + return new HtmlString($stylesheets->join('').$scripts->join('')); + } + + /** + * Generate a script tag for the given URL. + * + * @param string $url + * @return string + */ + protected function makeScriptTag($url) + { + return sprintf('', $url); + } + + /** + * Generate a stylesheet tag for the given URL. + * + * @param string $url + * @return string + */ + protected function makeStylesheetTag($url) + { + return sprintf('', $url); + } +} diff --git a/src/Illuminate/Foundation/helpers.php b/src/Illuminate/Foundation/helpers.php index 90a78ae0d76e..3c47c44f618d 100644 --- a/src/Illuminate/Foundation/helpers.php +++ b/src/Illuminate/Foundation/helpers.php @@ -15,6 +15,7 @@ use Illuminate\Foundation\Bus\PendingClosureDispatch; use Illuminate\Foundation\Bus\PendingDispatch; use Illuminate\Foundation\Mix; +use Illuminate\Foundation\Vite; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Queue\CallQueuedClosure; use Illuminate\Support\Facades\Date; @@ -522,6 +523,22 @@ function method_field($method) } } +if (! function_exists('vite')) { + /** + * Generate Vite tags for entrypoints. + * + * @param string|string[] $entrypoints + * @param string $buildDirectory + * @return \Illuminate\Support\HtmlString + * + * @throws \Exception + */ + function vite($entrypoints, $buildDirectory = 'build') + { + return app(Vite::class)(...func_get_args()); + } +} + if (! function_exists('mix')) { /** * Get the path to a versioned Mix file. diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php index 979cadc560cf..08625be17cda 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php @@ -46,4 +46,15 @@ protected function compileMethod($method) { return ""; } + + /** + * Compile the "vite" statements into valid PHP. + * + * @param string $arguments + * @return string + */ + protected function compileVite($arguments) + { + return ""; + } } diff --git a/tests/Foundation/FoundationHelpersTest.php b/tests/Foundation/FoundationHelpersTest.php index ddf06f83988e..363424d93126 100644 --- a/tests/Foundation/FoundationHelpersTest.php +++ b/tests/Foundation/FoundationHelpersTest.php @@ -43,6 +43,62 @@ public function testCache() $this->assertSame('default', cache('baz', 'default')); } + public function testViteWithoutCss() + { + $this->makeViteManifest(); + + $result = vite(['resources/js/app-without-css.js']); + + $this->cleanViteManifest(); + + $this->assertSame('', $result->toHtml()); + } + + public function testViteWithCss() + { + $this->makeViteManifest(); + + $result = vite(['resources/js/app-with-css.js']); + + $this->cleanViteManifest(); + + $this->assertSame( + '' + . '', + $result->toHtml() + ); + } + + public function testViteWithSharedCss() + { + $this->makeViteManifest(); + + $result = vite(['resources/js/app-with-shared-css.js']); + + $this->cleanViteManifest(); + + $this->assertSame( + '' + . '', + $result->toHtml() + ); + } + + public function testViteHotModuleReplacement() + { + $this->makeViteHotFile(); + + $result = vite(['resources/js/app-with-css.js']); + + $this->cleanViteHotFile(); + + $this->assertSame( + '' + . '', + $result->toHtml() + ); + } + public function testMixDoesNotIncludeHost() { $app = new Application; @@ -244,4 +300,56 @@ public function testMixIsSwappableForTests() $this->assertSame('expected', mix('asset.png')); } + + protected function makeViteManifest() + { + app()->singleton('path.public', fn () => __DIR__); + + if (! file_exists(public_path('build'))) { + mkdir(public_path('build')); + } + + $manifest = json_encode([ + 'resources/js/app-without-css.js' => [ + 'file' => 'assets/app-without-css.versioned.js', + ], + 'resources/js/app-with-css.js' => [ + 'file' => 'assets/app-with-css.versioned.js', + 'css' => [ + 'assets/app.versioned.css', + ], + ], + 'resources/js/app-with-shared-css.js' => [ + 'file' => 'assets/app-with-shared-css.versioned.js', + 'imports' => [ + '_someFile.js', + ], + ], + '_someFile.js' => [ + 'css' => [ + 'assets/app.versioned.css', + ], + ], + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + + file_put_contents(public_path('build/manifest.json'), $manifest); + } + + private function cleanViteManifest() + { + unlink(public_path('build/manifest.json')); + rmdir(public_path('build')); + } + + private function makeViteHotFile() + { + app()->singleton('path.public', fn () => __DIR__); + + file_put_contents(public_path('hot'), 'http://localhost:3000'); + } + + private function cleanViteHotFile() + { + unlink(public_path('hot')); + } } diff --git a/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php b/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php index dc03f098b42d..d8ada1031676 100644 --- a/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php +++ b/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php @@ -3,11 +3,32 @@ namespace Illuminate\Tests\Foundation\Testing\Concerns; use Illuminate\Foundation\Mix; +use Illuminate\Foundation\Vite; use Orchestra\Testbench\TestCase; use stdClass; class InteractsWithContainerTest extends TestCase { + public function testWithoutViteBindsEmptyHandlerAndReturnsInstance() + { + $instance = $this->withoutVite(); + + $this->assertSame('', vite(['resources/js/app.js'])); + $this->assertSame($this, $instance); + } + + public function testWithViteRestoresOriginalHandlerAndReturnsInstance() + { + $handler = new stdClass; + $this->app->instance(Vite::class, $handler); + + $this->withoutVite(); + $instance = $this->withVite(); + + $this->assertSame($handler, resolve(Vite::class)); + $this->assertSame($this, $instance); + } + public function testWithoutMixBindsEmptyHandlerAndReturnsInstance() { $instance = $this->withoutMix(); diff --git a/tests/View/Blade/BladeHelpersTest.php b/tests/View/Blade/BladeHelpersTest.php index 77e9522ce454..30e2ee879390 100644 --- a/tests/View/Blade/BladeHelpersTest.php +++ b/tests/View/Blade/BladeHelpersTest.php @@ -11,5 +11,6 @@ public function testEchosAreCompiled() $this->assertSame('', $this->compiler->compileString('@dd($var1)')); $this->assertSame('', $this->compiler->compileString('@dd($var1, $var2)')); $this->assertSame('', $this->compiler->compileString('@dump($var1, $var2)')); + $this->assertSame('', $this->compiler->compileString('@vite([\'resources/js/app.js\'])')); } } From af9058aaf162f3c602ec62966e06bc3e5dafb693 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 11 May 2022 14:19:47 +1000 Subject: [PATCH 02/14] Remove vite helper --- src/Illuminate/Foundation/helpers.php | 16 --- .../Compilers/Concerns/CompilesHelpers.php | 6 +- tests/Foundation/FoundationHelpersTest.php | 108 ---------------- tests/Foundation/FoundationViteTest.php | 116 ++++++++++++++++++ .../Concerns/InteractsWithContainerTest.php | 2 +- tests/View/Blade/BladeHelpersTest.php | 2 +- 6 files changed, 123 insertions(+), 127 deletions(-) create mode 100644 tests/Foundation/FoundationViteTest.php diff --git a/src/Illuminate/Foundation/helpers.php b/src/Illuminate/Foundation/helpers.php index 3c47c44f618d..5906bd8ad4fd 100644 --- a/src/Illuminate/Foundation/helpers.php +++ b/src/Illuminate/Foundation/helpers.php @@ -523,22 +523,6 @@ function method_field($method) } } -if (! function_exists('vite')) { - /** - * Generate Vite tags for entrypoints. - * - * @param string|string[] $entrypoints - * @param string $buildDirectory - * @return \Illuminate\Support\HtmlString - * - * @throws \Exception - */ - function vite($entrypoints, $buildDirectory = 'build') - { - return app(Vite::class)(...func_get_args()); - } -} - if (! function_exists('mix')) { /** * Get the path to a versioned Mix file. diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php index 08625be17cda..36bbc2532313 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php @@ -2,6 +2,8 @@ namespace Illuminate\View\Compilers\Concerns; +use Illuminate\Foundation\Vite; + trait CompilesHelpers { /** @@ -55,6 +57,8 @@ protected function compileMethod($method) */ protected function compileVite($arguments) { - return ""; + $class = Vite::class; + + return ""; } } diff --git a/tests/Foundation/FoundationHelpersTest.php b/tests/Foundation/FoundationHelpersTest.php index 363424d93126..ddf06f83988e 100644 --- a/tests/Foundation/FoundationHelpersTest.php +++ b/tests/Foundation/FoundationHelpersTest.php @@ -43,62 +43,6 @@ public function testCache() $this->assertSame('default', cache('baz', 'default')); } - public function testViteWithoutCss() - { - $this->makeViteManifest(); - - $result = vite(['resources/js/app-without-css.js']); - - $this->cleanViteManifest(); - - $this->assertSame('', $result->toHtml()); - } - - public function testViteWithCss() - { - $this->makeViteManifest(); - - $result = vite(['resources/js/app-with-css.js']); - - $this->cleanViteManifest(); - - $this->assertSame( - '' - . '', - $result->toHtml() - ); - } - - public function testViteWithSharedCss() - { - $this->makeViteManifest(); - - $result = vite(['resources/js/app-with-shared-css.js']); - - $this->cleanViteManifest(); - - $this->assertSame( - '' - . '', - $result->toHtml() - ); - } - - public function testViteHotModuleReplacement() - { - $this->makeViteHotFile(); - - $result = vite(['resources/js/app-with-css.js']); - - $this->cleanViteHotFile(); - - $this->assertSame( - '' - . '', - $result->toHtml() - ); - } - public function testMixDoesNotIncludeHost() { $app = new Application; @@ -300,56 +244,4 @@ public function testMixIsSwappableForTests() $this->assertSame('expected', mix('asset.png')); } - - protected function makeViteManifest() - { - app()->singleton('path.public', fn () => __DIR__); - - if (! file_exists(public_path('build'))) { - mkdir(public_path('build')); - } - - $manifest = json_encode([ - 'resources/js/app-without-css.js' => [ - 'file' => 'assets/app-without-css.versioned.js', - ], - 'resources/js/app-with-css.js' => [ - 'file' => 'assets/app-with-css.versioned.js', - 'css' => [ - 'assets/app.versioned.css', - ], - ], - 'resources/js/app-with-shared-css.js' => [ - 'file' => 'assets/app-with-shared-css.versioned.js', - 'imports' => [ - '_someFile.js', - ], - ], - '_someFile.js' => [ - 'css' => [ - 'assets/app.versioned.css', - ], - ], - ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - - file_put_contents(public_path('build/manifest.json'), $manifest); - } - - private function cleanViteManifest() - { - unlink(public_path('build/manifest.json')); - rmdir(public_path('build')); - } - - private function makeViteHotFile() - { - app()->singleton('path.public', fn () => __DIR__); - - file_put_contents(public_path('hot'), 'http://localhost:3000'); - } - - private function cleanViteHotFile() - { - unlink(public_path('hot')); - } } diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php new file mode 100644 index 000000000000..d90b8c608056 --- /dev/null +++ b/tests/Foundation/FoundationViteTest.php @@ -0,0 +1,116 @@ +makeViteManifest(); + + $result = (new Vite)(['resources/js/app-without-css.js']); + + $this->cleanViteManifest(); + + $this->assertSame('', $result->toHtml()); + } + + public function testViteWithCss() + { + $this->makeViteManifest(); + + $result = (new Vite)(['resources/js/app-with-css.js']); + + $this->cleanViteManifest(); + + $this->assertSame( + '' + . '', + $result->toHtml() + ); + } + + public function testViteWithSharedCss() + { + $this->makeViteManifest(); + + $result = (new Vite)(['resources/js/app-with-shared-css.js']); + + $this->cleanViteManifest(); + + $this->assertSame( + '' + . '', + $result->toHtml() + ); + } + + public function testViteHotModuleReplacement() + { + $this->makeViteHotFile(); + + $result = (new Vite)(['resources/js/app-with-css.js']); + + $this->cleanViteHotFile(); + + $this->assertSame( + '' + . '', + $result->toHtml() + ); + } + + protected function makeViteManifest() + { + app()->singleton('path.public', fn () => __DIR__); + + if (! file_exists(public_path('build'))) { + mkdir(public_path('build')); + } + + $manifest = json_encode([ + 'resources/js/app-without-css.js' => [ + 'file' => 'assets/app-without-css.versioned.js', + ], + 'resources/js/app-with-css.js' => [ + 'file' => 'assets/app-with-css.versioned.js', + 'css' => [ + 'assets/app.versioned.css', + ], + ], + 'resources/js/app-with-shared-css.js' => [ + 'file' => 'assets/app-with-shared-css.versioned.js', + 'imports' => [ + '_someFile.js', + ], + ], + '_someFile.js' => [ + 'css' => [ + 'assets/app.versioned.css', + ], + ], + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + + file_put_contents(public_path('build/manifest.json'), $manifest); + } + + protected function cleanViteManifest() + { + unlink(public_path('build/manifest.json')); + rmdir(public_path('build')); + } + + protected function makeViteHotFile() + { + app()->singleton('path.public', fn () => __DIR__); + + file_put_contents(public_path('hot'), 'http://localhost:3000'); + } + + protected function cleanViteHotFile() + { + unlink(public_path('hot')); + } +} diff --git a/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php b/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php index d8ada1031676..7dbcc2e05ef9 100644 --- a/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php +++ b/tests/Foundation/Testing/Concerns/InteractsWithContainerTest.php @@ -13,7 +13,7 @@ public function testWithoutViteBindsEmptyHandlerAndReturnsInstance() { $instance = $this->withoutVite(); - $this->assertSame('', vite(['resources/js/app.js'])); + $this->assertSame('', app(Vite::class)(['resources/js/app.js'])); $this->assertSame($this, $instance); } diff --git a/tests/View/Blade/BladeHelpersTest.php b/tests/View/Blade/BladeHelpersTest.php index 30e2ee879390..486fd9c544e3 100644 --- a/tests/View/Blade/BladeHelpersTest.php +++ b/tests/View/Blade/BladeHelpersTest.php @@ -11,6 +11,6 @@ public function testEchosAreCompiled() $this->assertSame('', $this->compiler->compileString('@dd($var1)')); $this->assertSame('', $this->compiler->compileString('@dd($var1, $var2)')); $this->assertSame('', $this->compiler->compileString('@dump($var1, $var2)')); - $this->assertSame('', $this->compiler->compileString('@vite([\'resources/js/app.js\'])')); + $this->assertSame('', $this->compiler->compileString('@vite([\'resources/js/app.js\'])')); } } From c706959e72c27247c53debe8a103d17929db9661 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Mon, 16 May 2022 14:19:44 +1000 Subject: [PATCH 03/14] Add @viteReactRefresh directive --- src/Illuminate/Foundation/Vite.php | 24 +++++++++++++++++++ .../Compilers/Concerns/CompilesHelpers.php | 12 ++++++++++ tests/View/Blade/BladeHelpersTest.php | 1 + 3 files changed, 37 insertions(+) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 599781515a1f..91c683b7178a 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -79,6 +79,30 @@ public function __invoke($entrypoints, $buildDirectory = 'build') return new HtmlString($stylesheets->join('').$scripts->join('')); } + public function reactRefresh() + { + if (! is_file(public_path('/hot'))) { + return; + } + + $url = rtrim(file_get_contents(public_path('/hot'))); + + return new HtmlString( + sprintf( + <<<'HTML' + + HTML, + $url + ) + ); + } + /** * Generate a script tag for the given URL. * diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php index 36bbc2532313..73d076a1026e 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php @@ -61,4 +61,16 @@ protected function compileVite($arguments) return ""; } + + /** + * Compile the "viteReactRefresh" statements into valid PHP. + * + * @return string + */ + protected function compileViteReactRefresh() + { + $class = Vite::class; + + return "reactRefresh(); ?>"; + } } diff --git a/tests/View/Blade/BladeHelpersTest.php b/tests/View/Blade/BladeHelpersTest.php index 486fd9c544e3..ee48cdf2199c 100644 --- a/tests/View/Blade/BladeHelpersTest.php +++ b/tests/View/Blade/BladeHelpersTest.php @@ -12,5 +12,6 @@ public function testEchosAreCompiled() $this->assertSame('', $this->compiler->compileString('@dd($var1, $var2)')); $this->assertSame('', $this->compiler->compileString('@dump($var1, $var2)')); $this->assertSame('', $this->compiler->compileString('@vite([\'resources/js/app.js\'])')); + $this->assertSame('reactRefresh(); ?>', $this->compiler->compileString('@viteReactRefresh')); } } From 1df8145adcdf0751ec82240c81b19846e60a0145 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Mon, 16 May 2022 16:35:33 +1000 Subject: [PATCH 04/14] Add docblock --- src/Illuminate/Foundation/Vite.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 91c683b7178a..004c82913104 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -79,6 +79,11 @@ public function __invoke($entrypoints, $buildDirectory = 'build') return new HtmlString($stylesheets->join('').$scripts->join('')); } + /** + * Generate React refresh runtime script + * + * @return \Illuminate\Support\HtmlString|void + */ public function reactRefresh() { if (! is_file(public_path('/hot'))) { From 8c629c52f2f455c741c23775b39afef7b190ab63 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Mon, 16 May 2022 16:35:42 +1000 Subject: [PATCH 05/14] Remove unused import --- src/Illuminate/Foundation/helpers.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Foundation/helpers.php b/src/Illuminate/Foundation/helpers.php index 5906bd8ad4fd..90a78ae0d76e 100644 --- a/src/Illuminate/Foundation/helpers.php +++ b/src/Illuminate/Foundation/helpers.php @@ -15,7 +15,6 @@ use Illuminate\Foundation\Bus\PendingClosureDispatch; use Illuminate\Foundation\Bus\PendingDispatch; use Illuminate\Foundation\Mix; -use Illuminate\Foundation\Vite; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Queue\CallQueuedClosure; use Illuminate\Support\Facades\Date; From f4c99558a18f552be3507631baea72cc60c0d743 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Thu, 19 May 2022 10:31:36 +1000 Subject: [PATCH 06/14] Automatically clean up after tests --- tests/Foundation/FoundationViteTest.php | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index d90b8c608056..aa8142e3dc77 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -6,14 +6,18 @@ class FoundationViteTest extends TestCase { + protected function tearDown(): void + { + $this->cleanViteManifest(); + $this->cleanViteHotFile(); + } + public function testViteWithoutCss() { $this->makeViteManifest(); $result = (new Vite)(['resources/js/app-without-css.js']); - $this->cleanViteManifest(); - $this->assertSame('', $result->toHtml()); } @@ -23,8 +27,6 @@ public function testViteWithCss() $result = (new Vite)(['resources/js/app-with-css.js']); - $this->cleanViteManifest(); - $this->assertSame( '' . '', @@ -38,8 +40,6 @@ public function testViteWithSharedCss() $result = (new Vite)(['resources/js/app-with-shared-css.js']); - $this->cleanViteManifest(); - $this->assertSame( '' . '', @@ -53,8 +53,6 @@ public function testViteHotModuleReplacement() $result = (new Vite)(['resources/js/app-with-css.js']); - $this->cleanViteHotFile(); - $this->assertSame( '' . '', @@ -98,8 +96,13 @@ protected function makeViteManifest() protected function cleanViteManifest() { - unlink(public_path('build/manifest.json')); - rmdir(public_path('build')); + if (file_exists(public_path('build/manifest.json'))) { + unlink(public_path('build/manifest.json')); + } + + if (file_exists(public_path('build'))) { + rmdir(public_path('build')); + } } protected function makeViteHotFile() @@ -111,6 +114,8 @@ protected function makeViteHotFile() protected function cleanViteHotFile() { - unlink(public_path('hot')); + if (file_exists(public_path('hot'))) { + unlink(public_path('hot')); + } } } From 8e04ae59a19e5e9123885ce0f05131158920220e Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Thu, 19 May 2022 10:33:01 +1000 Subject: [PATCH 07/14] Use ASSET_URL when resolving assets --- src/Illuminate/Foundation/Vite.php | 10 +++++++--- tests/Foundation/FoundationViteTest.php | 23 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 004c82913104..6cdeb2759a46 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -56,12 +56,14 @@ public function __invoke($entrypoints, $buildDirectory = 'build') } $scripts->push( - $this->makeScriptTag("{$buildDirectory}/{$manifest[$entrypoint]['file']}") + $this->makeScriptTag(asset("{$buildDirectory}/{$manifest[$entrypoint]['file']}")) ); if (isset($manifest[$entrypoint]['css'])) { foreach ($manifest[$entrypoint]['css'] as $css) { - $stylesheets->push($this->makeStylesheetTag("{$buildDirectory}/{$css}")); + $stylesheets->push( + $this->makeStylesheetTag(asset("{$buildDirectory}/{$css}")) + ); } } @@ -69,7 +71,9 @@ public function __invoke($entrypoints, $buildDirectory = 'build') foreach ($manifest[$entrypoint]['imports'] as $import) { if (isset($manifest[$import]['css'])) { foreach ($manifest[$import]['css'] as $css) { - $stylesheets->push($this->makeStylesheetTag("{$buildDirectory}/{$css}")); + $stylesheets->push( + $this->makeStylesheetTag(asset("{$buildDirectory}/{$css}")) + ); } } } diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index aa8142e3dc77..8979c71fe631 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -2,14 +2,27 @@ namespace Illuminate\Foundation; +use Illuminate\Routing\UrlGenerator; +use Mockery as m; use PHPUnit\Framework\TestCase; class FoundationViteTest extends TestCase { + protected function setUp(): void + { + app()->instance('url', tap( + m::mock(UrlGenerator::class), + fn ($url) => $url + ->shouldReceive('asset') + ->andReturnUsing(fn ($value) => "https://example.com{$value}") + )); + } + protected function tearDown(): void { $this->cleanViteManifest(); $this->cleanViteHotFile(); + m::close(); } public function testViteWithoutCss() @@ -18,7 +31,7 @@ public function testViteWithoutCss() $result = (new Vite)(['resources/js/app-without-css.js']); - $this->assertSame('', $result->toHtml()); + $this->assertSame('', $result->toHtml()); } public function testViteWithCss() @@ -28,8 +41,8 @@ public function testViteWithCss() $result = (new Vite)(['resources/js/app-with-css.js']); $this->assertSame( - '' - . '', + '' + . '', $result->toHtml() ); } @@ -41,8 +54,8 @@ public function testViteWithSharedCss() $result = (new Vite)(['resources/js/app-with-shared-css.js']); $this->assertSame( - '' - . '', + '' + . '', $result->toHtml() ); } From 4d3b30f2cbd5f95a78c223b4925bb8f28a8b5624 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 25 May 2022 16:24:47 +1000 Subject: [PATCH 08/14] support default entrypoint --- src/Illuminate/Foundation/Vite.php | 2 +- .../View/Compilers/Concerns/CompilesHelpers.php | 2 ++ tests/Foundation/FoundationViteTest.php | 15 +++++++++++++++ tests/View/Blade/BladeHelpersTest.php | 3 +++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 6cdeb2759a46..4eed1e522064 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -17,7 +17,7 @@ class Vite * * @throws \Exception */ - public function __invoke($entrypoints, $buildDirectory = 'build') + public function __invoke($entrypoints = 'resources/js/app.js', $buildDirectory = 'build') { static $manifests = []; diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php index 73d076a1026e..bb3a517b0723 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php @@ -57,6 +57,8 @@ protected function compileMethod($method) */ protected function compileVite($arguments) { + $arguments ??= '()'; + $class = Vite::class; return ""; diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 8979c71fe631..67017c95372f 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -25,6 +25,18 @@ protected function tearDown(): void m::close(); } + public function testViteWithDefaultEntrypoint() + { + $this->makeViteManifest(); + + $result = (new Vite)(); + + $this->assertSame( + '', + $result->toHtml() + ); + } + public function testViteWithoutCss() { $this->makeViteManifest(); @@ -82,6 +94,9 @@ protected function makeViteManifest() } $manifest = json_encode([ + 'resources/js/app.js' => [ + 'file' => 'assets/app.versioned.js', + ], 'resources/js/app-without-css.js' => [ 'file' => 'assets/app-without-css.versioned.js', ], diff --git a/tests/View/Blade/BladeHelpersTest.php b/tests/View/Blade/BladeHelpersTest.php index ee48cdf2199c..8e071c38b6c6 100644 --- a/tests/View/Blade/BladeHelpersTest.php +++ b/tests/View/Blade/BladeHelpersTest.php @@ -11,6 +11,9 @@ public function testEchosAreCompiled() $this->assertSame('', $this->compiler->compileString('@dd($var1)')); $this->assertSame('', $this->compiler->compileString('@dd($var1, $var2)')); $this->assertSame('', $this->compiler->compileString('@dump($var1, $var2)')); + $this->assertSame('', $this->compiler->compileString('@vite')); + $this->assertSame('', $this->compiler->compileString('@vite()')); + $this->assertSame('', $this->compiler->compileString('@vite(\'resources/js/app.js\')')); $this->assertSame('', $this->compiler->compileString('@vite([\'resources/js/app.js\'])')); $this->assertSame('reactRefresh(); ?>', $this->compiler->compileString('@viteReactRefresh')); } From 1769f727337a166f9d1e80d834c35e3a0dcc895c Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Wed, 25 May 2022 16:26:07 +1000 Subject: [PATCH 09/14] update docblock --- src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php index bb3a517b0723..6c6fcd996830 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php @@ -52,7 +52,7 @@ protected function compileMethod($method) /** * Compile the "vite" statements into valid PHP. * - * @param string $arguments + * @param ?string $arguments * @return string */ protected function compileVite($arguments) From f744692000c4a44cc4a97bfca74809405d519951 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 1 Jun 2022 15:03:03 +1000 Subject: [PATCH 10/14] Linting --- src/Illuminate/Foundation/Vite.php | 2 +- tests/Foundation/FoundationViteTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 4eed1e522064..7ef4b0c249e0 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -84,7 +84,7 @@ public function __invoke($entrypoints = 'resources/js/app.js', $buildDirectory = } /** - * Generate React refresh runtime script + * Generate React refresh runtime script. * * @return \Illuminate\Support\HtmlString|void */ diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 67017c95372f..eeb52da77eaf 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -54,7 +54,7 @@ public function testViteWithCss() $this->assertSame( '' - . '', + .'', $result->toHtml() ); } @@ -67,7 +67,7 @@ public function testViteWithSharedCss() $this->assertSame( '' - . '', + .'', $result->toHtml() ); } @@ -80,7 +80,7 @@ public function testViteHotModuleReplacement() $this->assertSame( '' - . '', + .'', $result->toHtml() ); } From d5ffd6ca7c25dbd24914529486794494a4f3f84b Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Thu, 9 Jun 2022 11:32:13 +1000 Subject: [PATCH 11/14] Support CSS entry points --- src/Illuminate/Foundation/Vite.php | 44 ++++++++++++++++++------- tests/Foundation/FoundationViteTest.php | 38 ++++++++++++++++++--- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 7ef4b0c249e0..d32f0357a2ee 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -29,7 +29,7 @@ public function __invoke($entrypoints = 'resources/js/app.js', $buildDirectory = return new HtmlString( $entrypoints - ->map(fn ($entrypoint) => $this->makeScriptTag("{$url}/{$entrypoint}")) + ->map(fn ($entrypoint) => $this->makeTag("{$url}/{$entrypoint}")) ->prepend($this->makeScriptTag("{$url}/@vite/client")) ->join('') ); @@ -47,23 +47,18 @@ public function __invoke($entrypoints = 'resources/js/app.js', $buildDirectory = $manifest = $manifests[$manifestPath]; - $scripts = collect(); - $stylesheets = collect(); + $tags = collect(); foreach ($entrypoints as $entrypoint) { if (! isset($manifest[$entrypoint])) { throw new Exception("Unable to locate file in Vite manifest: {$entrypoint}."); } - $scripts->push( - $this->makeScriptTag(asset("{$buildDirectory}/{$manifest[$entrypoint]['file']}")) - ); + $tags->push($this->makeTag(asset("{$buildDirectory}/{$manifest[$entrypoint]['file']}"))); if (isset($manifest[$entrypoint]['css'])) { foreach ($manifest[$entrypoint]['css'] as $css) { - $stylesheets->push( - $this->makeStylesheetTag(asset("{$buildDirectory}/{$css}")) - ); + $tags->push($this->makeStylesheetTag(asset("{$buildDirectory}/{$css}"))); } } @@ -71,15 +66,15 @@ public function __invoke($entrypoints = 'resources/js/app.js', $buildDirectory = foreach ($manifest[$entrypoint]['imports'] as $import) { if (isset($manifest[$import]['css'])) { foreach ($manifest[$import]['css'] as $css) { - $stylesheets->push( - $this->makeStylesheetTag(asset("{$buildDirectory}/{$css}")) - ); + $tags->push($this->makeStylesheetTag(asset("{$buildDirectory}/{$css}"))); } } } } } + [$stylesheets, $scripts] = $tags->partition(fn ($tag) => str_starts_with($tag, 'join('').$scripts->join('')); } @@ -112,6 +107,21 @@ public function reactRefresh() ); } + /** + * Generate an appropriate tag for the given URL. + * + * @param string $url + * @return string + */ + protected function makeTag($url) + { + if ($this->isCssPath($url)) { + return $this->makeStylesheetTag($url); + } + + return $this->makeScriptTag($url); + } + /** * Generate a script tag for the given URL. * @@ -133,4 +143,14 @@ protected function makeStylesheetTag($url) { return sprintf('', $url); } + + /** + * Determine whether the given path is a CSS file + * + * @param string $path + * @return bool + */ + protected function isCssPath($path) { + return preg_match('/\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/', $path) === 1; + } } diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index eeb52da77eaf..4c1031b16ad7 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -46,7 +46,7 @@ public function testViteWithoutCss() $this->assertSame('', $result->toHtml()); } - public function testViteWithCss() + public function testViteWithCssImport() { $this->makeViteManifest(); @@ -59,7 +59,7 @@ public function testViteWithCss() ); } - public function testViteWithSharedCss() + public function testViteWithSharedCssImport() { $this->makeViteManifest(); @@ -72,15 +72,42 @@ public function testViteWithSharedCss() ); } + public function testViteWithCssEntrypoint() + { + $this->makeViteManifest(); + + $result = (new Vite)(['resources/js/app.js', 'resources/css/app.css']); + + $this->assertSame( + '' + .'', + $result->toHtml() + ); + } + public function testViteHotModuleReplacement() { $this->makeViteHotFile(); - $result = (new Vite)(['resources/js/app-with-css.js']); + $result = (new Vite)(['resources/js/app.js']); $this->assertSame( '' - .'', + .'', + $result->toHtml() + ); + } + + public function testViteHotModuleReplacementWithCssEntrypoint() + { + $this->makeViteHotFile(); + + $result = (new Vite)(['resources/css/app.css', 'resources/js/app.js']); + + $this->assertSame( + '' + .'' + .'', $result->toHtml() ); } @@ -112,6 +139,9 @@ protected function makeViteManifest() '_someFile.js', ], ], + 'resources/css/app.css' => [ + 'file' => 'assets/app.versioned.css', + ], '_someFile.js' => [ 'css' => [ 'assets/app.versioned.css', From 013ec2768a42c92af9a6946ff8bdd19a427b4968 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Fri, 10 Jun 2022 16:57:54 +1000 Subject: [PATCH 12/14] Remove default entry points Blade apps and SPAs have different entry point requirements so there is no sensible default. --- src/Illuminate/Foundation/Vite.php | 7 +-- tests/Foundation/FoundationViteTest.php | 57 +++++++++---------------- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index d32f0357a2ee..7e641ab7b606 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -17,7 +17,7 @@ class Vite * * @throws \Exception */ - public function __invoke($entrypoints = 'resources/js/app.js', $buildDirectory = 'build') + public function __invoke($entrypoints, $buildDirectory = 'build') { static $manifests = []; @@ -145,12 +145,13 @@ protected function makeStylesheetTag($url) } /** - * Determine whether the given path is a CSS file + * Determine whether the given path is a CSS file. * * @param string $path * @return bool */ - protected function isCssPath($path) { + protected function isCssPath($path) + { return preg_match('/\.(css|less|sass|scss|styl|stylus|pcss|postcss)$/', $path) === 1; } } diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 4c1031b16ad7..5d6894024425 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -25,71 +25,59 @@ protected function tearDown(): void m::close(); } - public function testViteWithDefaultEntrypoint() + public function testViteWithJsOnly() { $this->makeViteManifest(); - $result = (new Vite)(); + $result = (new Vite)('resources/js/app.js'); - $this->assertSame( - '', - $result->toHtml() - ); - } - - public function testViteWithoutCss() - { - $this->makeViteManifest(); - - $result = (new Vite)(['resources/js/app-without-css.js']); - - $this->assertSame('', $result->toHtml()); + $this->assertSame('', $result->toHtml()); } - public function testViteWithCssImport() + public function testViteWithCssAndJs() { $this->makeViteManifest(); - $result = (new Vite)(['resources/js/app-with-css.js']); + $result = (new Vite)(['resources/css/app.css', 'resources/js/app.js']); $this->assertSame( '' - .'', + .'', $result->toHtml() ); } - public function testViteWithSharedCssImport() + public function testViteWithCssImport() { $this->makeViteManifest(); - $result = (new Vite)(['resources/js/app-with-shared-css.js']); + $result = (new Vite)('resources/js/app-with-css-import.js'); $this->assertSame( - '' - .'', + '' + .'', $result->toHtml() ); } - public function testViteWithCssEntrypoint() + public function testViteWithSharedCssImport() { $this->makeViteManifest(); - $result = (new Vite)(['resources/js/app.js', 'resources/css/app.css']); + $result = (new Vite)(['resources/js/app-with-shared-css.js']); $this->assertSame( - '' - .'', + '' + .'', $result->toHtml() ); } - public function testViteHotModuleReplacement() + public function testViteHotModuleReplacementWithJsOnly() { $this->makeViteHotFile(); - $result = (new Vite)(['resources/js/app.js']); + $result = (new Vite)('resources/js/app.js'); $this->assertSame( '' @@ -98,7 +86,7 @@ public function testViteHotModuleReplacement() ); } - public function testViteHotModuleReplacementWithCssEntrypoint() + public function testViteHotModuleReplacementWithJsAndCss() { $this->makeViteHotFile(); @@ -124,13 +112,10 @@ protected function makeViteManifest() 'resources/js/app.js' => [ 'file' => 'assets/app.versioned.js', ], - 'resources/js/app-without-css.js' => [ - 'file' => 'assets/app-without-css.versioned.js', - ], - 'resources/js/app-with-css.js' => [ - 'file' => 'assets/app-with-css.versioned.js', + 'resources/js/app-with-css-import.js' => [ + 'file' => 'assets/app-with-css-import.versioned.js', 'css' => [ - 'assets/app.versioned.css', + 'assets/imported-css.versioned.css', ], ], 'resources/js/app-with-shared-css.js' => [ @@ -144,7 +129,7 @@ protected function makeViteManifest() ], '_someFile.js' => [ 'css' => [ - 'assets/app.versioned.css', + 'assets/shared-css.versioned.css', ], ], ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); From 290d1c6561c25e9c67323c32da1d4dac6e3a5487 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Wed, 22 Jun 2022 12:11:52 +1000 Subject: [PATCH 13/14] Fix test namespace Co-authored-by: Lucas Michot <513603+lucasmichot@users.noreply.github.com> --- tests/Foundation/FoundationViteTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 5d6894024425..3c43dddfc86e 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -1,6 +1,6 @@ Date: Wed, 22 Jun 2022 12:20:34 +1000 Subject: [PATCH 14/14] Add missing import --- tests/Foundation/FoundationViteTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Foundation/FoundationViteTest.php b/tests/Foundation/FoundationViteTest.php index 3c43dddfc86e..4a9c5b61c91e 100644 --- a/tests/Foundation/FoundationViteTest.php +++ b/tests/Foundation/FoundationViteTest.php @@ -2,6 +2,7 @@ namespace Illuminate\Tests\Foundation; +use Illuminate\Foundation\Vite; use Illuminate\Routing\UrlGenerator; use Mockery as m; use PHPUnit\Framework\TestCase;