diff --git a/src/BladeRenderer.php b/src/BladeRenderer.php index 9a308736..8004b61a 100644 --- a/src/BladeRenderer.php +++ b/src/BladeRenderer.php @@ -2,19 +2,22 @@ namespace Livewire\Blaze; +use Closure; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Arr; use Illuminate\Support\Facades\File; use Illuminate\View\Compilers\BladeCompiler; -use Illuminate\View\Component; use Illuminate\View\ComponentSlot; +use Livewire\Blaze\Exceptions\InvalidBlazeFoldUsageException; use Livewire\Blaze\Parser\Attribute; use Livewire\Blaze\Parser\Nodes\ComponentNode; use Livewire\Blaze\Parser\Nodes\SlotNode; use Livewire\Blaze\Runtime\BlazeRuntime; -use Livewire\Blaze\Support\ComponentSource; use Livewire\Blaze\Support\Utils; use ReflectionClass; +use ReflectionFunction; + +use function Livewire\invade; /** * Handles isolated Blade rendering used during compile-time folding. @@ -79,6 +82,16 @@ function ($input) { return $input; }, ], + 'precompilers' => fn (array $precompilers) => [ + ...$this->withoutLivewirePrecompilers($precompilers), + function (string $input) use ($path) { + if (preg_match('~<\s*livewire[-:]|(? null, 'forElseCounter' => 0, 'firstCaseInSwitch' => true, @@ -173,10 +186,12 @@ protected function freezeObjectProperties(object|string $object, array $properti $property = $reflection->getProperty($name); - $frozen[$name] = $property->getValue(is_object($object) ? $object : null); + $currentValue = $property->getValue(is_object($object) ? $object : null); + + $frozen[$name] = $currentValue; if (! is_numeric($key)) { - $property->setValue($object, $value); + $property->setValue($object, $value instanceof Closure ? $value($currentValue) : $value); } } @@ -187,4 +202,39 @@ protected function freezeObjectProperties(object|string $object, array $properti } }; } + + /** + * Filter out Livewire-specific precompilers. + */ + protected function withoutLivewirePrecompilers(array $precompilers): array + { + if (! class_exists(\Livewire\Livewire::class)) { + return $precompilers; + } + + $livewireOnlyPrecompilers = class_exists(\Livewire\Mechanisms\ExtendBlade\ExtendBlade::class) + ? invade(app(\Livewire\Mechanisms\ExtendBlade\ExtendBlade::class))->precompilers + : []; + + return Arr::where($precompilers, function ($precompiler) use ($livewireOnlyPrecompilers) { + if ($precompiler instanceof \Livewire\Mechanisms\CompileLivewireTags\LivewireTagPrecompiler) { + return false; + } + + if (in_array($precompiler, $livewireOnlyPrecompilers, true)) { + return false; + } + + if ($precompiler instanceof Closure && in_array((new ReflectionFunction($precompiler))->getClosureScopeClass()?->getName(), [ + \Livewire\Features\SupportCompiledWireKeys\SupportCompiledWireKeys::class, + \Livewire\Features\SupportMorphAwareBladeCompilation\SupportMorphAwareBladeCompilation::class, + \Livewire\Mechanisms\ExtendBlade\ExtendBlade::class, + \Livewire\LivewireServiceProvider::class, + ], true)) { + return false; + } + + return true; + }); + } } diff --git a/src/Exceptions/InvalidBlazeFoldUsageException.php b/src/Exceptions/InvalidBlazeFoldUsageException.php index e2aae66b..b71a9419 100644 --- a/src/Exceptions/InvalidBlazeFoldUsageException.php +++ b/src/Exceptions/InvalidBlazeFoldUsageException.php @@ -127,4 +127,12 @@ public static function forOnce(string $componentPath): self ); } + public static function forLivewire(string $componentPath): self + { + return new self( + $componentPath, + ' 'forAuth', 'request\\(\\)' => 'forRequest', 'old\\(' => 'forOld', + ' 'forLivewire', + '@livewire' => 'forLivewire', ]; foreach ($problematicPatterns as $pattern => $factoryMethod) { diff --git a/tests/Folder/FolderTest.php b/tests/Folder/FolderTest.php index c0a0889b..8dfa1849 100644 --- a/tests/Folder/FolderTest.php +++ b/tests/Folder/FolderTest.php @@ -301,7 +301,7 @@ expect(fn () => app(Folder::class)->fold($node)) ->toThrow(InvalidBlazeFoldUsageException::class); -})->with(['errors', 'session', 'error', 'csrf', 'auth', 'request', 'old', 'once']); +})->with(['errors', 'session', 'error', 'csrf', 'auth', 'request', 'old', 'once', 'livewire', 'livewire-directive', 'livewire-wrapped']); test('does not fold components with slots wrapped in directives', function () { $input = '@if(false)Header@endif'; diff --git a/tests/fixtures/views/components/invalid-foldable/livewire-directive.blade.php b/tests/fixtures/views/components/invalid-foldable/livewire-directive.blade.php new file mode 100644 index 00000000..6b849516 --- /dev/null +++ b/tests/fixtures/views/components/invalid-foldable/livewire-directive.blade.php @@ -0,0 +1,3 @@ +@blaze(fold: true) + +@livewire('counter') diff --git a/tests/fixtures/views/components/invalid-foldable/livewire-wrapped.blade.php b/tests/fixtures/views/components/invalid-foldable/livewire-wrapped.blade.php new file mode 100644 index 00000000..0cf8c29f --- /dev/null +++ b/tests/fixtures/views/components/invalid-foldable/livewire-wrapped.blade.php @@ -0,0 +1,3 @@ +@blaze(fold: true) + + diff --git a/tests/fixtures/views/components/invalid-foldable/livewire.blade.php b/tests/fixtures/views/components/invalid-foldable/livewire.blade.php new file mode 100644 index 00000000..072db7aa --- /dev/null +++ b/tests/fixtures/views/components/invalid-foldable/livewire.blade.php @@ -0,0 +1,3 @@ +@blaze(fold: true) + +