From 56db49397aa54531ea9bad32c481e665afbfb071 Mon Sep 17 00:00:00 2001 From: Wim Verhoogt Date: Fri, 28 Jul 2023 17:09:44 +0200 Subject: [PATCH 1/9] Livewire v3 migratie --- README.md | 34 +++++++--- composer.json | 4 +- phpunit.xml.dist | 13 ++-- src/EntangleNode.php | 21 +++++++ src/EntangleTokenParser.php | 23 +++++++ src/LivewireExtension.php | 45 +++++++++++-- src/LivewireNode.php | 23 ++++--- src/LivewireTokenParser.php | 52 +++++++-------- src/LivewireTwigServiceProvider.php | 3 + src/PersistNode.php | 24 +++++++ src/PersistTokenParser.php | 31 +++++++++ src/ThisNode.php | 25 ++++++++ src/ThisTokenParser.php | 22 +++++++ tests/RenderTest.php | 63 +++++++++++++++++-- tests/views/components/entangle.twig | 6 ++ tests/views/components/this.twig | 8 +++ tests/views/entangle-test.twig | 5 ++ tests/views/name-type-test.twig | 6 +- tests/views/this-test.twig | 6 ++ ...-test.twig => unknown-component-test.twig} | 0 20 files changed, 350 insertions(+), 64 deletions(-) create mode 100644 src/EntangleNode.php create mode 100644 src/EntangleTokenParser.php create mode 100644 src/PersistNode.php create mode 100644 src/PersistTokenParser.php create mode 100644 src/ThisNode.php create mode 100644 src/ThisTokenParser.php create mode 100644 tests/views/components/entangle.twig create mode 100644 tests/views/components/this.twig create mode 100644 tests/views/entangle-test.twig create mode 100644 tests/views/this-test.twig rename tests/views/{invalid-type-test.twig => unknown-component-test.twig} (100%) diff --git a/README.md b/README.md index 58bf38d..d1eb642 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ The `enflow/livewire-twig` package provides the option to load Livewire components in your Twig templates. +## Update notes +This version supports Livewire 3. +The name argument for {% livewire %} and the other directives is now interpreted as an expression, allowing the use of variables or Twig expressions as a name. Note that for this reason a constant name now must be enclosed in quotes. + ## Installation You can install the package via composer: @@ -17,12 +21,10 @@ composer require enflow/livewire-twig The Twig extension will automatically register when `rcrowe/twigbridge` is used. If you're using another configuration, you may wish to register the extension manually by loading the extension `Enflow\LivewireTwig\LivewireExtension`. -This package only provides a wrapper for the `@livewireScripts`, `@livewireStyles` & `@livewire` calls. Everything else under the hood is powered by `livewire/livewire`. +This package provides wrappers for the `@livewireScripts`, `@livewireStyles`, `@livewireScriptConfig`, `@livewire`, `@entangle`, `@this` and `@persist`, directives. Everything else under the hood is powered by `livewire/livewire`. You can register your Livewire components like normal. -## Installation - -Add the following tags in the `head` tag, and before the end `body` tag in your template. +To use Livewire, add the following tags in the `head` tag, and before the end `body` tag in your template. ```twig @@ -41,13 +43,32 @@ In your body you may include the component like: ```twig {# The Twig version of '@livewire' #} -{% livewire counter %} +{% livewire 'counter' %} {# If you wish to pass along variables to your component #} -{% livewire counter with {'count': 3} %} +{% livewire 'counter' with {'count': 3} %} {# To include a nested component (or dashes), you need to use '' #} {% livewire 'nested.component' %} + +{# To use key tracking, you need to use key() #} +{% livewire 'counter' key('counterkey') %} + +{# The Twig version of '@persist' #} +{% persist 'name' %} +
+ ... +
+{% endpersist %} + +{# The Twig version of '@entangle' (as of Livewire 3.01 poorly documented, need to check the source code) #} +{% entangle 'expression' %} + +{# The Twig version of '@this' (Can only be used after Livewire initialization is complete) #} +{% this %} + +{# The Twig version of '@livewireScriptConfig' (as of Livewire 3.01 poorly documented, need to check the source code) #} +{{ livewireScriptConfig() }} ``` ### Example @@ -86,7 +107,6 @@ class Counter extends Component ``` ## Todo -- [ ] Implement support for `key` tracking - [ ] Moar tests. ## Testing diff --git a/composer.json b/composer.json index 4c31d9f..feefc5f 100644 --- a/composer.json +++ b/composer.json @@ -10,12 +10,12 @@ ], "require": { "php": "^8.2", - "livewire/livewire": "^2.2" + "livewire/livewire": "^3.0" }, "require-dev": { "laravel/pint": "^1.0", "orchestra/testbench": "^8.0", - "phpunit/phpunit": "^10.0", + "phpunit/phpunit": "^10.1", "rcrowe/twigbridge": "^0.14.1" }, "suggest": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 87c5e2d..f1504c7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,14 @@ - - - - src/ - - + + tests + + + src/ + + diff --git a/src/EntangleNode.php b/src/EntangleNode.php new file mode 100644 index 0000000..f7f7fcd --- /dev/null +++ b/src/EntangleNode.php @@ -0,0 +1,21 @@ +getEnvironment()->getExtension(LivewireExtension::class); + $livewire = new NameExpression("__livewire", $this->lineno); + $compiler + ->write('$__livewire = ')->subcompile($livewire)->raw(";\n") + ->write('$expression = ')->subcompile($this->getNode('EntangleValue'))->raw(";\n") + ->write("\$instance_id = \"window.Livewire.find('{\$__livewire->getId()}')\";\n") + ->write("if ((object) (\$expression) instanceof \\Livewire\\WireDirective) echo \"\$instance_id.entangle(\$expression->value()).\$expression->hasModifier('live') ? '.live' : ''\"; else echo \"\$instance_id.entangle('\$expression')\";")->raw("\n"); + } +} diff --git a/src/EntangleTokenParser.php b/src/EntangleTokenParser.php new file mode 100644 index 0000000..07e6ed5 --- /dev/null +++ b/src/EntangleTokenParser.php @@ -0,0 +1,23 @@ +getLine(); + $stream = $this->parser->getStream(); + $entVar = $this->parser->getExpressionParser()->parseExpression(); + $stream->expect(Token::BLOCK_END_TYPE); + + return new EntangleNode(['EntangleValue' => $entVar], [], $lineno, $this->getTag()); + } + public function getTag(): string + { + return 'entangle'; + } +} diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index 189c7f0..188c166 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -2,34 +2,67 @@ namespace Enflow\LivewireTwig; -use Livewire\Livewire; +use Illuminate\Support\Facades\Blade; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; +use Livewire\Mechanisms\FrontendAssets\FrontendAssets; class LivewireExtension extends AbstractExtension { + protected $calls; + protected $dirs = [ + 'livewireScripts', + 'livewireScriptConfig', + 'livewireStyles', + 'this', + 'livewire', + 'persist', + 'endpersist', + 'entangle' + ]; + + public function __construct() + { + $dirs = Blade::getCustomDirectives(); + $this->calls = collect($this->dirs)->mapWithKeys(function($e) use ($dirs) { return [$e => $dirs[$e]]; }); + } + + public function callDirective(string $directive, array $args = []) : string + { + $call = $this->calls[$directive]; + $r = call_user_func_array($call, $args); + return "?> $r ['html']]), new TwigFunction('livewireScripts', [$this, 'livewireScripts'], ['is_safe' => ['html']]), + new TwigFunction('livewireScriptConfig', [$this, 'livewireScriptConfig'], ['is_safe' => ['html']]), ]; } - public function livewireStyles() + public function livewireStyles($args = '') { - return Livewire::styles(); + return FrontendAssets::styles($args); } - - public function livewireScripts() + public function livewireScripts($args = '') + { + return FrontendAssets::scripts($args); + } + public function livewireScriptConfig($args = []) { - return Livewire::scripts(); + return FrontendAssets::scriptConfig($args); } public function getTokenParsers(): array { return [ new LivewireTokenParser(), + new PersistTokenParser(), + new ThisTokenParser(), + new EntangleTokenParser() ]; } } diff --git a/src/LivewireNode.php b/src/LivewireNode.php index ca1d6e3..8f90da3 100644 --- a/src/LivewireNode.php +++ b/src/LivewireNode.php @@ -2,28 +2,35 @@ namespace Enflow\LivewireTwig; -use Livewire\LivewireBladeDirectives; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Node; class LivewireNode extends Node { - public function __construct(string $component, AbstractExpression $variables, int $lineno, string $tag = null) + public function __construct(Node $component, array $attributes, int $lineno, string $tag = null) { - $nodes = ['variables' => $variables]; + $nodes = ['variables' => $attributes['variables'], 'key' => $attributes['key']]; parent::__construct($nodes, ['component' => $component], $lineno, $tag); } public function compile(Compiler $compiler) { + $ext = $compiler->getEnvironment()->getExtension(LivewireExtension::class); $component = $this->getAttribute('component'); $expr = $this->getNode('variables'); + $key = $this->getNode('key'); + $hasKey = !$key->hasAttribute('value') || $key->getAttribute('value') !== ''; $compiler - ->write('$_instance = $context["_instance"] ?? null;', "\n") - ->write('$_vars = ')->subcompile($expr)->raw(";\n") - ->write("?>\n") - ->write(LivewireBladeDirectives::livewire("'$component', \$_vars")) - ->write("write('$_name = ')->subcompile($component)->raw(";\n") + ->write('$_vars = ')->subcompile($expr)->raw(";\n"); + if ($hasKey) { + $compiler + ->write('$_key = ')->subcompile($key)->raw(";\n") + ->write($ext->callDirective('livewire', ['$_name, $_vars, key($_key)'])); + } else { + $compiler + ->write($ext->callDirective('livewire', ['$_name, $_vars'])); + } } } diff --git a/src/LivewireTokenParser.php b/src/LivewireTokenParser.php index b8ed6ce..1ee35fb 100644 --- a/src/LivewireTokenParser.php +++ b/src/LivewireTokenParser.php @@ -5,6 +5,7 @@ use Illuminate\Support\Str; use Twig\Error\SyntaxError; use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\ConstantExpression; use Twig\Token; use Twig\TokenParser\AbstractTokenParser; @@ -12,33 +13,32 @@ class LivewireTokenParser extends AbstractTokenParser { public function parse(Token $token): LivewireNode { - $componentNameToken = $this->parser->getStream()->next(); - - if ($componentNameToken->test(Token::NAME_TYPE) || $componentNameToken->test(Token::STRING_TYPE)) { - $component = $componentNameToken->getValue(); - } else { - throw new SyntaxError( - sprintf( - 'Unexpected token "%s"%s ("%s" or "%s" expected).', - Token::typeToEnglish($componentNameToken->getType()), - $componentNameToken->getValue() ? sprintf(' of value "%s"', $componentNameToken->getValue()) : '', - Token::typeToEnglish(Token::NAME_TYPE), - Token::typeToEnglish(Token::STRING_TYPE) - ), - $componentNameToken->getLine(), - $this->parser->getStream()->getSourceContext() - ); - } - - if ($this->parser->getStream()->nextIf(/* Token::NAME_TYPE */ 5, 'with')) { - $variables = $this->parser->getExpressionParser()->parseExpression(); - } else { - $variables = new ArrayExpression([], $token->getLine()); + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $component = $this->parser->getExpressionParser()->parseExpression(); + + $end = false; + $variables = new ArrayExpression([], $token->getLine()); + $key = new ConstantExpression('', $lineno); + while (!$end) { + $n = $stream->next(); + if ($n->test(Token::NAME_TYPE, 'with')) { + $variables = $this->parser->getExpressionParser()->parseExpression(); + } + elseif ($n->test(Token::NAME_TYPE, 'key')) { + $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, '('); + $key = $this->parser->getExpressionParser()->parseExpression(); + $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')'); + } + elseif ($n->test(Token::BLOCK_END_TYPE)) { + $end = true; + } + else { + throw new SyntaxError(sprintf('Unexpected end of template. Twig was expecting the end of the directive starting at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } } - - $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); - - return new LivewireNode(Str::kebab($component), $variables, $token->getLine(), $this->getTag()); + $attrs = ['variables' => $variables, 'key' => $key]; + return new LivewireNode($component, $attrs, $token->getLine(), $this->getTag()); } public function getTag(): string diff --git a/src/LivewireTwigServiceProvider.php b/src/LivewireTwigServiceProvider.php index 4dccc80..20f9d7a 100644 --- a/src/LivewireTwigServiceProvider.php +++ b/src/LivewireTwigServiceProvider.php @@ -2,7 +2,10 @@ namespace Enflow\LivewireTwig; +use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; +use Illuminate\Support\Facades\View; +use Illuminate\Support\Facades\Blade; class LivewireTwigServiceProvider extends ServiceProvider { diff --git a/src/PersistNode.php b/src/PersistNode.php new file mode 100644 index 0000000..9132702 --- /dev/null +++ b/src/PersistNode.php @@ -0,0 +1,24 @@ +getEnvironment()->getExtension(LivewireExtension::class); + $compiler + ->write('$__name = ')->subcompile($this->attributes['name'])->raw(";\n") + ->write($ext->callDirective('persist', ['$__name']))->raw("\n") + ->subcompile($this->nodes[0]) + ->write($ext->callDirective('endpersist', ['$__name']))->raw("\n"); + } +} diff --git a/src/PersistTokenParser.php b/src/PersistTokenParser.php new file mode 100644 index 0000000..57e830f --- /dev/null +++ b/src/PersistTokenParser.php @@ -0,0 +1,31 @@ +getLine(); + $stream = $this->parser->getStream(); + $name = $this->parser->getExpressionParser()->parseExpression(); //$stream->next(); + $stream->expect(Token::BLOCK_END_TYPE); + $body = $this->parser->subparse([$this, 'endPersist']); + $stream->next(); + $stream->expect(Token::BLOCK_END_TYPE); + + return new PersistNode([$body], ['name' => $name], $lineno, $this->getTag()); + } + + public function endPersist(Token $token) + { + return $token->test(['endpersist']); + } + public function getTag(): string + { + return 'persist'; + } +} diff --git a/src/ThisNode.php b/src/ThisNode.php new file mode 100644 index 0000000..1faae14 --- /dev/null +++ b/src/ThisNode.php @@ -0,0 +1,25 @@ +getEnvironment()->getExtension(LivewireExtension::class); + $livewire = new NameExpression("__livewire", $this->lineno); + $compiler + ->write('$_instance = ')->subcompile($livewire)->raw(";\n") + ->write("echo \"window.Livewire.find('{\$_instance->getId()}')\"")->raw(";\n"); + // ->write($ext->callDirective('this', []))->raw("\n"); + } +} diff --git a/src/ThisTokenParser.php b/src/ThisTokenParser.php new file mode 100644 index 0000000..5ac9f71 --- /dev/null +++ b/src/ThisTokenParser.php @@ -0,0 +1,22 @@ +getLine(); + $stream = $this->parser->getStream(); + $stream->expect(Token::BLOCK_END_TYPE); + + return new ThisNode([], [], $lineno, $this->getTag()); + } + public function getTag(): string + { + return 'this'; + } +} diff --git a/tests/RenderTest.php b/tests/RenderTest.php index cc38a32..5a6768b 100644 --- a/tests/RenderTest.php +++ b/tests/RenderTest.php @@ -14,7 +14,7 @@ public function test_name_type_component_correctly_renders() $rendered = view('name-type-test')->render(); $this->assertStringContainsString('[wire\:loading]', $rendered); // Styles - $this->assertStringContainsString('window.livewire', $rendered); // Scripts + $this->assertStringContainsString('script src="/livewire/livewire.js', $rendered); // Scripts $this->assertStringContainsString('increment', $rendered); // Counter component $this->assertStringContainsString('Lorem ipsum!', $rendered); // Counter component title } @@ -26,7 +26,7 @@ public function test_string_type_component_correctly_renders() $rendered = view('string-type-test')->render(); $this->assertStringContainsString('[wire\:loading]', $rendered); // Styles - $this->assertStringContainsString('window.livewire', $rendered); // Scripts + $this->assertStringContainsString('script src="/livewire/livewire.js', $rendered); // Scripts $this->assertStringContainsString('increment', $rendered); // Counter component $this->assertStringContainsString('Lorem ipsum!', $rendered); // Counter component title } @@ -44,13 +44,37 @@ public function test_nested_component_correctly_renders() $this->assertStringContainsString('Bar', $rendered); } - public function test_invalid_type_throws_exception() + public function test_unknown_component_throws_exception() { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Unexpected token "number" of value "63" ("name" or "string" expected).'); + $this->expectException(\ErrorException::class); + $this->expectExceptionMessage('Unable to find component: [63]'); Livewire::component('counter', Counter::class); - $rendered = view('invalid-type-test')->render(); + $rendered = view('unknown-component-test')->render(); + } + + public function test_entangle() + { + Livewire::component('entangle', Entangle::class); + + $rendered = view('entangle-test')->render(); + $this->assertStringContainsString('entangle(\'entangled\')', $rendered); + } + + public function test_this() + { + Livewire::component('this', This::class); + + $rendered = view('this-test')->render(); + $this->assertStringContainsString('window.Livewire.find(\'', $rendered); + } + + public function test_config() + { + Livewire::component('this', This::class); + + $rendered = view('this-test')->render(); + $this->assertStringContainsString('window.livewireScriptConfig', $rendered); } } @@ -85,3 +109,30 @@ public function render() return view('components.table.row'); } } +class This extends Component +{ + public $count = 1; + + public function increment() + { + $this->count++; + } + + public function decrement() + { + $this->count--; + } + + public function render() + { + return view('components.this'); + } +} +class Entangle extends Component +{ + public $entangle = 1; + public function render() + { + return view('components.entangle'); + } +} \ No newline at end of file diff --git a/tests/views/components/entangle.twig b/tests/views/components/entangle.twig new file mode 100644 index 0000000..7bd21ca --- /dev/null +++ b/tests/views/components/entangle.twig @@ -0,0 +1,6 @@ +
+ {% if title %} +

{{ title }}

+ {% endif %} + {% entangle 'entangled' %} +
\ No newline at end of file diff --git a/tests/views/components/this.twig b/tests/views/components/this.twig new file mode 100644 index 0000000..ece9b04 --- /dev/null +++ b/tests/views/components/this.twig @@ -0,0 +1,8 @@ +{% if title %} +

{{ title }}

+{% endif %} + + +

{{ count }}

+ + \ No newline at end of file diff --git a/tests/views/entangle-test.twig b/tests/views/entangle-test.twig new file mode 100644 index 0000000..e028fba --- /dev/null +++ b/tests/views/entangle-test.twig @@ -0,0 +1,5 @@ +
+ {% block content %} + {% livewire 'entangle' %} + {% endblock %} +
diff --git a/tests/views/name-type-test.twig b/tests/views/name-type-test.twig index f041bd6..b5b59af 100644 --- a/tests/views/name-type-test.twig +++ b/tests/views/name-type-test.twig @@ -1,9 +1,9 @@ {% extends 'layout' %} {% block content %} - {% livewire counter %} + {% livewire 'counter' %} - {% livewire counter with {'count': 3} %} + {% livewire 'counter' with {'count': 3} %} - {% livewire counter with {'count': 3, 'title': 'Lorem ipsum!'} %} + {% livewire 'counter' with {'count': 3, 'title': 'Lorem ipsum!'} %} {% endblock %} diff --git a/tests/views/this-test.twig b/tests/views/this-test.twig new file mode 100644 index 0000000..c8eed2e --- /dev/null +++ b/tests/views/this-test.twig @@ -0,0 +1,6 @@ +{% extends 'layout' %} + +{% block content %} + {% livewire 'this' %} + {{ livewireScriptConfig({'config': true }) }} +{% endblock %} diff --git a/tests/views/invalid-type-test.twig b/tests/views/unknown-component-test.twig similarity index 100% rename from tests/views/invalid-type-test.twig rename to tests/views/unknown-component-test.twig From 249ab6b918e19d420ea6b7794dca53221d1dd500 Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Wed, 13 Sep 2023 15:08:36 +0200 Subject: [PATCH 2/9] Add versions to README.md --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d1eb642..a297cc9 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,13 @@ The `enflow/livewire-twig` package provides the option to load Livewire components in your Twig templates. -## Update notes -This version supports Livewire 3. +## Versions + +### <= 3.x.x +Version 3.x.x supports Livewire 2. + +### >= 4.x.x +Version 4.xxx supports Livewire 3. The name argument for {% livewire %} and the other directives is now interpreted as an expression, allowing the use of variables or Twig expressions as a name. Note that for this reason a constant name now must be enclosed in quotes. ## Installation From 3eb2dcfa1a3cc3b5d6b3585e95a9ffb03241dadd Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Wed, 13 Sep 2023 15:11:46 +0200 Subject: [PATCH 3/9] Formatting --- src/EntangleNode.php | 2 +- src/EntangleTokenParser.php | 1 + src/LivewireExtension.php | 20 ++++++++++++-------- src/LivewireNode.php | 2 ++ src/LivewireTokenParser.php | 12 +++++------- src/LivewireTwigServiceProvider.php | 2 +- src/PersistNode.php | 6 +----- src/PersistTokenParser.php | 3 ++- src/ThisNode.php | 8 +------- src/ThisTokenParser.php | 1 + 10 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/EntangleNode.php b/src/EntangleNode.php index f7f7fcd..16b2faa 100644 --- a/src/EntangleNode.php +++ b/src/EntangleNode.php @@ -10,8 +10,8 @@ class EntangleNode extends Node { public function compile(Compiler $compiler) { - $ext = $compiler->getEnvironment()->getExtension(LivewireExtension::class); $livewire = new NameExpression("__livewire", $this->lineno); + $compiler ->write('$__livewire = ')->subcompile($livewire)->raw(";\n") ->write('$expression = ')->subcompile($this->getNode('EntangleValue'))->raw(";\n") diff --git a/src/EntangleTokenParser.php b/src/EntangleTokenParser.php index 07e6ed5..6080424 100644 --- a/src/EntangleTokenParser.php +++ b/src/EntangleTokenParser.php @@ -16,6 +16,7 @@ public function parse(Token $token): EntangleNode return new EntangleNode(['EntangleValue' => $entVar], [], $lineno, $this->getTag()); } + public function getTag(): string { return 'entangle'; diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index 188c166..2b4f17c 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -2,6 +2,7 @@ namespace Enflow\LivewireTwig; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Blade; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -9,8 +10,8 @@ class LivewireExtension extends AbstractExtension { - protected $calls; - protected $dirs = [ + protected Collection $calls; + protected array $dirs = [ 'livewireScripts', 'livewireScriptConfig', 'livewireStyles', @@ -23,11 +24,12 @@ class LivewireExtension extends AbstractExtension public function __construct() { - $dirs = Blade::getCustomDirectives(); - $this->calls = collect($this->dirs)->mapWithKeys(function($e) use ($dirs) { return [$e => $dirs[$e]]; }); + $customDirectives = Blade::getCustomDirectives(); + + $this->calls = collect($this->dirs)->mapWithKeys(fn($e) => [$e => $customDirectives[$e]]); } - public function callDirective(string $directive, array $args = []) : string + public function callDirective(string $directive, array $args = []): string { $call = $this->calls[$directive]; $r = call_user_func_array($call, $args); @@ -43,15 +45,17 @@ public function getFunctions(): array ]; } - public function livewireStyles($args = '') + public function livewireStyles($args = ''): string { return FrontendAssets::styles($args); } - public function livewireScripts($args = '') + + public function livewireScripts($args = ''): string { return FrontendAssets::scripts($args); } - public function livewireScriptConfig($args = []) + + public function livewireScriptConfig($args = []): string { return FrontendAssets::scriptConfig($args); } diff --git a/src/LivewireNode.php b/src/LivewireNode.php index 8f90da3..6a2b28f 100644 --- a/src/LivewireNode.php +++ b/src/LivewireNode.php @@ -21,9 +21,11 @@ public function compile(Compiler $compiler) $expr = $this->getNode('variables'); $key = $this->getNode('key'); $hasKey = !$key->hasAttribute('value') || $key->getAttribute('value') !== ''; + $compiler ->write('$_name = ')->subcompile($component)->raw(";\n") ->write('$_vars = ')->subcompile($expr)->raw(";\n"); + if ($hasKey) { $compiler ->write('$_key = ')->subcompile($key)->raw(";\n") diff --git a/src/LivewireTokenParser.php b/src/LivewireTokenParser.php index 1ee35fb..6b163c6 100644 --- a/src/LivewireTokenParser.php +++ b/src/LivewireTokenParser.php @@ -20,23 +20,21 @@ public function parse(Token $token): LivewireNode $end = false; $variables = new ArrayExpression([], $token->getLine()); $key = new ConstantExpression('', $lineno); - while (!$end) { + while (! $end) { $n = $stream->next(); if ($n->test(Token::NAME_TYPE, 'with')) { $variables = $this->parser->getExpressionParser()->parseExpression(); - } - elseif ($n->test(Token::NAME_TYPE, 'key')) { + } elseif ($n->test(Token::NAME_TYPE, 'key')) { $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, '('); $key = $this->parser->getExpressionParser()->parseExpression(); $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')'); - } - elseif ($n->test(Token::BLOCK_END_TYPE)) { + } elseif ($n->test(Token::BLOCK_END_TYPE)) { $end = true; - } - else { + } else { throw new SyntaxError(sprintf('Unexpected end of template. Twig was expecting the end of the directive starting at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } + $attrs = ['variables' => $variables, 'key' => $key]; return new LivewireNode($component, $attrs, $token->getLine(), $this->getTag()); } diff --git a/src/LivewireTwigServiceProvider.php b/src/LivewireTwigServiceProvider.php index 20f9d7a..06d9637 100644 --- a/src/LivewireTwigServiceProvider.php +++ b/src/LivewireTwigServiceProvider.php @@ -9,7 +9,7 @@ class LivewireTwigServiceProvider extends ServiceProvider { - public function boot() + public function boot(): void { $this->app->afterResolving('twig', fn () => $this->app['twig']->addExtension(new LivewireExtension())); } diff --git a/src/PersistNode.php b/src/PersistNode.php index 9132702..b5564bd 100644 --- a/src/PersistNode.php +++ b/src/PersistNode.php @@ -7,14 +7,10 @@ class PersistNode extends Node { - // public function __construct(Node $body, int $lineno, string $tag) - // { - // parent::__construct([$body], [], $lineno, $tag); - // } - public function compile(Compiler $compiler) { $ext = $compiler->getEnvironment()->getExtension(LivewireExtension::class); + $compiler ->write('$__name = ')->subcompile($this->attributes['name'])->raw(";\n") ->write($ext->callDirective('persist', ['$__name']))->raw("\n") diff --git a/src/PersistTokenParser.php b/src/PersistTokenParser.php index 57e830f..705ddef 100644 --- a/src/PersistTokenParser.php +++ b/src/PersistTokenParser.php @@ -20,10 +20,11 @@ public function parse(Token $token): PersistNode return new PersistNode([$body], ['name' => $name], $lineno, $this->getTag()); } - public function endPersist(Token $token) + public function endPersist(Token $token): bool { return $token->test(['endpersist']); } + public function getTag(): string { return 'persist'; diff --git a/src/ThisNode.php b/src/ThisNode.php index 1faae14..cb8e6ed 100644 --- a/src/ThisNode.php +++ b/src/ThisNode.php @@ -8,18 +8,12 @@ class ThisNode extends Node { - // public function __construct(Node $body, int $lineno, string $tag) - // { - // parent::__construct([$body], [], $lineno, $tag); - // } - public function compile(Compiler $compiler) { - // $ext = $compiler->getEnvironment()->getExtension(LivewireExtension::class); $livewire = new NameExpression("__livewire", $this->lineno); + $compiler ->write('$_instance = ')->subcompile($livewire)->raw(";\n") ->write("echo \"window.Livewire.find('{\$_instance->getId()}')\"")->raw(";\n"); - // ->write($ext->callDirective('this', []))->raw("\n"); } } diff --git a/src/ThisTokenParser.php b/src/ThisTokenParser.php index 5ac9f71..33841e1 100644 --- a/src/ThisTokenParser.php +++ b/src/ThisTokenParser.php @@ -15,6 +15,7 @@ public function parse(Token $token): ThisNode return new ThisNode([], [], $lineno, $this->getTag()); } + public function getTag(): string { return 'this'; From deaa18241dc3b61928805a914c4f226e01a2d7e8 Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Wed, 13 Sep 2023 16:19:08 +0200 Subject: [PATCH 4/9] Formatting, cleanup --- tests/views/components/this.twig | 7 ------- tests/views/layout.twig | 2 +- tests/views/this-test.twig | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/views/components/this.twig b/tests/views/components/this.twig index ece9b04..cc3e07a 100644 --- a/tests/views/components/this.twig +++ b/tests/views/components/this.twig @@ -1,8 +1 @@ -{% if title %} -

{{ title }}

-{% endif %} - - -

{{ count }}

- \ No newline at end of file diff --git a/tests/views/layout.twig b/tests/views/layout.twig index 14525ed..6514462 100644 --- a/tests/views/layout.twig +++ b/tests/views/layout.twig @@ -2,4 +2,4 @@ {% block content %}{% endblock %} -{{ livewireScripts() }} +{{ livewireScripts() }} \ No newline at end of file diff --git a/tests/views/this-test.twig b/tests/views/this-test.twig index c8eed2e..fc7277f 100644 --- a/tests/views/this-test.twig +++ b/tests/views/this-test.twig @@ -2,5 +2,5 @@ {% block content %} {% livewire 'this' %} - {{ livewireScriptConfig({'config': true }) }} + {{ livewireScriptConfig({'config': true}) }} {% endblock %} From 332f71d1b959a9de71aceb81f4756169c04b3dfa Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Wed, 13 Sep 2023 16:19:36 +0200 Subject: [PATCH 5/9] Move token parsers & nodes to namespace --- src/LivewireExtension.php | 8 ++++---- src/{ => Nodes}/EntangleNode.php | 2 +- src/{ => Nodes}/LivewireNode.php | 5 +++-- src/{ => Nodes}/PersistNode.php | 3 ++- src/{ => Nodes}/ThisNode.php | 2 +- src/{ => TokenParsers}/EntangleTokenParser.php | 3 ++- src/{ => TokenParsers}/LivewireTokenParser.php | 3 ++- src/{ => TokenParsers}/PersistTokenParser.php | 5 +++-- src/{ => TokenParsers}/ThisTokenParser.php | 3 ++- 9 files changed, 20 insertions(+), 14 deletions(-) rename src/{ => Nodes}/EntangleNode.php (95%) rename src/{ => Nodes}/LivewireNode.php (93%) rename src/{ => Nodes}/PersistNode.php (86%) rename src/{ => Nodes}/ThisNode.php (92%) rename src/{ => TokenParsers}/EntangleTokenParser.php (86%) rename src/{ => TokenParsers}/LivewireTokenParser.php (95%) rename src/{ => TokenParsers}/PersistTokenParser.php (89%) rename src/{ => TokenParsers}/ThisTokenParser.php (84%) diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index 2b4f17c..e35e607 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -63,10 +63,10 @@ public function livewireScriptConfig($args = []): string public function getTokenParsers(): array { return [ - new LivewireTokenParser(), - new PersistTokenParser(), - new ThisTokenParser(), - new EntangleTokenParser() + new TokenParsers\LivewireTokenParser(), + new TokenParsers\PersistTokenParser(), + new TokenParsers\ThisTokenParser(), + new TokenParsers\EntangleTokenParser() ]; } } diff --git a/src/EntangleNode.php b/src/Nodes/EntangleNode.php similarity index 95% rename from src/EntangleNode.php rename to src/Nodes/EntangleNode.php index 16b2faa..16eaec7 100644 --- a/src/EntangleNode.php +++ b/src/Nodes/EntangleNode.php @@ -1,6 +1,6 @@ getEnvironment()->getExtension(LivewireExtension::class); + $component = $this->getAttribute('component'); $expr = $this->getNode('variables'); $key = $this->getNode('key'); diff --git a/src/PersistNode.php b/src/Nodes/PersistNode.php similarity index 86% rename from src/PersistNode.php rename to src/Nodes/PersistNode.php index b5564bd..7f6a12c 100644 --- a/src/PersistNode.php +++ b/src/Nodes/PersistNode.php @@ -1,7 +1,8 @@ getLine(); $stream = $this->parser->getStream(); - $name = $this->parser->getExpressionParser()->parseExpression(); //$stream->next(); + $name = $this->parser->getExpressionParser()->parseExpression(); $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'endPersist']); $stream->next(); diff --git a/src/ThisTokenParser.php b/src/TokenParsers/ThisTokenParser.php similarity index 84% rename from src/ThisTokenParser.php rename to src/TokenParsers/ThisTokenParser.php index 33841e1..7173b30 100644 --- a/src/ThisTokenParser.php +++ b/src/TokenParsers/ThisTokenParser.php @@ -1,7 +1,8 @@ Date: Fri, 22 Sep 2023 19:34:29 +0200 Subject: [PATCH 6/9] Move to dot notation --- README.md | 21 +++-- src/LivewireExtension.php | 20 +---- src/LivewireTokenParser.php | 109 +++++++++++++++++++++++ src/TokenParsers/EntangleTokenParser.php | 25 ------ src/TokenParsers/LivewireTokenParser.php | 47 ---------- src/TokenParsers/PersistTokenParser.php | 33 ------- src/TokenParsers/ThisTokenParser.php | 24 ----- tests/RenderTest.php | 31 ++++--- tests/views/components/entangle.twig | 2 +- tests/views/components/haschilds.twig | 2 +- tests/views/components/persist.twig | 5 ++ tests/views/components/table.twig | 2 +- tests/views/components/this.twig | 4 +- tests/views/entangle-test.twig | 2 +- tests/views/name-type-test.twig | 6 +- tests/views/nested-test.twig | 6 +- tests/views/persist-test.twig | 6 ++ tests/views/string-type-test.twig | 6 +- tests/views/this-test.twig | 2 +- tests/views/unknown-component-test.twig | 2 +- 20 files changed, 173 insertions(+), 182 deletions(-) create mode 100644 src/LivewireTokenParser.php delete mode 100644 src/TokenParsers/EntangleTokenParser.php delete mode 100644 src/TokenParsers/LivewireTokenParser.php delete mode 100644 src/TokenParsers/PersistTokenParser.php delete mode 100644 src/TokenParsers/ThisTokenParser.php create mode 100644 tests/views/components/persist.twig create mode 100644 tests/views/persist-test.twig diff --git a/README.md b/README.md index a297cc9..6f7d972 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,10 @@ Version 3.x.x supports Livewire 2. ### >= 4.x.x Version 4.xxx supports Livewire 3. -The name argument for {% livewire %} and the other directives is now interpreted as an expression, allowing the use of variables or Twig expressions as a name. Note that for this reason a constant name now must be enclosed in quotes. + +This version changes from the `{% livewire.component test %}` syntax to the `{% livewire.component 'test' %}` syntax. + +The name argument for {% livewire.component %} and the other directives is now interpreted as an expression, allowing the use of variables or Twig expressions as a name. Note that for this reason a constant name now must be enclosed in quotes. ## Installation You can install the package via composer: @@ -48,29 +51,29 @@ In your body you may include the component like: ```twig {# The Twig version of '@livewire' #} -{% livewire 'counter' %} +{% livewire.component 'counter' %} {# If you wish to pass along variables to your component #} -{% livewire 'counter' with {'count': 3} %} +{% livewire.component 'counter' with {'count': 3} %} {# To include a nested component (or dashes), you need to use '' #} -{% livewire 'nested.component' %} +{% livewire.component 'nested.component' %} {# To use key tracking, you need to use key() #} -{% livewire 'counter' key('counterkey') %} +{% livewire.component 'counter' key('counterkey') %} {# The Twig version of '@persist' #} -{% persist 'name' %} +{% livewire.persist 'name' %}
...
-{% endpersist %} +{% livewire.endpersist %} {# The Twig version of '@entangle' (as of Livewire 3.01 poorly documented, need to check the source code) #} -{% entangle 'expression' %} +{% livewire.entangle 'expression' %} {# The Twig version of '@this' (Can only be used after Livewire initialization is complete) #} -{% this %} +{% livewire.this %} {# The Twig version of '@livewireScriptConfig' (as of Livewire 3.01 poorly documented, need to check the source code) #} {{ livewireScriptConfig() }} diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index e35e607..a8891a3 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -15,23 +15,14 @@ class LivewireExtension extends AbstractExtension 'livewireScripts', 'livewireScriptConfig', 'livewireStyles', - 'this', 'livewire', - 'persist', - 'endpersist', - 'entangle' ]; - public function __construct() - { - $customDirectives = Blade::getCustomDirectives(); - - $this->calls = collect($this->dirs)->mapWithKeys(fn($e) => [$e => $customDirectives[$e]]); - } - public function callDirective(string $directive, array $args = []): string { - $call = $this->calls[$directive]; + $directives = Blade::getCustomDirectives(); + $call = $directives[$directive] ?? null; + $r = call_user_func_array($call, $args); return "?> $r getLine(); + $stream = $this->parser->getStream(); + + // Expect the '.' after `livewire` + $stream->expect(Token::PUNCTUATION_TYPE, '.'); + + // Detect the type after the dot + $type = $stream->expect(Token::NAME_TYPE)->getValue(); + + return (match ($type) { + 'this' => function () use ($stream, $lineno) { + $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block + return new ThisNode([], [], $lineno, $this->getTag()); + }, + + 'entangle' => function () use ($stream, $lineno) { + $entVar = $this->parser->getExpressionParser()->parseExpression(); + $stream->expect(Token::BLOCK_END_TYPE); + + return new EntangleNode(['EntangleValue' => $entVar], [], $lineno, $this->getTag()); + }, + + 'persist' => function () use ($stream, $lineno) { + // Parse the name. + $name = $this->parser->getExpressionParser()->parseExpression(); + + // Expect the end block for the start tag. + $stream->expect(Token::BLOCK_END_TYPE); + + // Parse the body until the 'endpersist' tag. + $body = $this->parser->subparse(function (Token $token) use ($stream) { + return $token->test('livewire'); + }, true); + + // Now, since we know we're at a 'livewire.' token, we should expect the 'endpersist' after it. + $stream->expect(Token::PUNCTUATION_TYPE, '.'); + + if (!$stream->test(Token::NAME_TYPE, 'endpersist')) { + throw new SyntaxError('Expected the "endpersist" tag.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + + $stream->next(); // Consume the 'endpersist' token + $stream->expect(Token::BLOCK_END_TYPE); + + return new PersistNode([$body], ['name' => $name], $lineno, $this->getTag()); + }, + + 'component' => function () use ($stream, $lineno) { + // Proceed with parsing the livewire.component + $component = $this->parser->getExpressionParser()->parseExpression(); + + $variables = new ArrayExpression([], $lineno); + $key = new ConstantExpression('', $lineno); + + while (! $stream->test(Token::BLOCK_END_TYPE)) { + if ($stream->test(Token::NAME_TYPE, 'with')) { + $stream->next(); // Consume the 'with' token + $variables = $this->parser->getExpressionParser()->parseExpression(); + } elseif ($stream->test(Token::NAME_TYPE, 'key')) { + $stream->next(); // Consume the 'key' token + $stream->expect(Token::PUNCTUATION_TYPE, '('); + $key = $this->parser->getExpressionParser()->parseExpression(); + $stream->expect(Token::PUNCTUATION_TYPE, ')'); + } else { + throw new SyntaxError(sprintf('Unexpected token in livewire tag. Twig was expecting the end of the directive starting at line %d).', $lineno), $lineno, $stream->getSourceContext()); + } + } + + $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block + + $attrs = ['variables' => $variables, 'key' => $key]; + return new LivewireNode($component, $attrs, $lineno, $this->getTag()); + }, + + default => fn() => throw new SyntaxError(sprintf('Unexpected token after "livewire.". Expected %s but got "%s".', implode(' or ', $this->types), $type), $lineno, $stream->getSourceContext()), + })(); + } + + public function getTag(): string + { + return 'livewire'; + } +} \ No newline at end of file diff --git a/src/TokenParsers/EntangleTokenParser.php b/src/TokenParsers/EntangleTokenParser.php deleted file mode 100644 index 7f051f2..0000000 --- a/src/TokenParsers/EntangleTokenParser.php +++ /dev/null @@ -1,25 +0,0 @@ -getLine(); - $stream = $this->parser->getStream(); - $entVar = $this->parser->getExpressionParser()->parseExpression(); - $stream->expect(Token::BLOCK_END_TYPE); - - return new EntangleNode(['EntangleValue' => $entVar], [], $lineno, $this->getTag()); - } - - public function getTag(): string - { - return 'entangle'; - } -} diff --git a/src/TokenParsers/LivewireTokenParser.php b/src/TokenParsers/LivewireTokenParser.php deleted file mode 100644 index d9f5874..0000000 --- a/src/TokenParsers/LivewireTokenParser.php +++ /dev/null @@ -1,47 +0,0 @@ -getLine(); - $stream = $this->parser->getStream(); - $component = $this->parser->getExpressionParser()->parseExpression(); - - $end = false; - $variables = new ArrayExpression([], $token->getLine()); - $key = new ConstantExpression('', $lineno); - while (! $end) { - $n = $stream->next(); - if ($n->test(Token::NAME_TYPE, 'with')) { - $variables = $this->parser->getExpressionParser()->parseExpression(); - } elseif ($n->test(Token::NAME_TYPE, 'key')) { - $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, '('); - $key = $this->parser->getExpressionParser()->parseExpression(); - $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')'); - } elseif ($n->test(Token::BLOCK_END_TYPE)) { - $end = true; - } else { - throw new SyntaxError(sprintf('Unexpected end of template. Twig was expecting the end of the directive starting at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()); - } - } - - $attrs = ['variables' => $variables, 'key' => $key]; - return new LivewireNode($component, $attrs, $token->getLine(), $this->getTag()); - } - - public function getTag(): string - { - return 'livewire'; - } -} diff --git a/src/TokenParsers/PersistTokenParser.php b/src/TokenParsers/PersistTokenParser.php deleted file mode 100644 index 9f32e65..0000000 --- a/src/TokenParsers/PersistTokenParser.php +++ /dev/null @@ -1,33 +0,0 @@ -getLine(); - $stream = $this->parser->getStream(); - $name = $this->parser->getExpressionParser()->parseExpression(); - $stream->expect(Token::BLOCK_END_TYPE); - $body = $this->parser->subparse([$this, 'endPersist']); - $stream->next(); - $stream->expect(Token::BLOCK_END_TYPE); - - return new PersistNode([$body], ['name' => $name], $lineno, $this->getTag()); - } - - public function endPersist(Token $token): bool - { - return $token->test(['endpersist']); - } - - public function getTag(): string - { - return 'persist'; - } -} diff --git a/src/TokenParsers/ThisTokenParser.php b/src/TokenParsers/ThisTokenParser.php deleted file mode 100644 index 7173b30..0000000 --- a/src/TokenParsers/ThisTokenParser.php +++ /dev/null @@ -1,24 +0,0 @@ -getLine(); - $stream = $this->parser->getStream(); - $stream->expect(Token::BLOCK_END_TYPE); - - return new ThisNode([], [], $lineno, $this->getTag()); - } - - public function getTag(): string - { - return 'this'; - } -} diff --git a/tests/RenderTest.php b/tests/RenderTest.php index 5a6768b..ced9032 100644 --- a/tests/RenderTest.php +++ b/tests/RenderTest.php @@ -69,6 +69,14 @@ public function test_this() $this->assertStringContainsString('window.Livewire.find(\'', $rendered); } + public function test_persist() + { + Livewire::component('persist', Persist::class); + + $rendered = view('persist-test')->render(); + $this->assertStringContainsString('x-persist=', $rendered); + } + public function test_config() { Livewire::component('this', This::class); @@ -109,30 +117,29 @@ public function render() return view('components.table.row'); } } + class This extends Component { - public $count = 1; - - public function increment() - { - $this->count++; - } - - public function decrement() - { - $this->count--; - } - public function render() { return view('components.this'); } } + class Entangle extends Component { public $entangle = 1; + public function render() { return view('components.entangle'); } +} + +class Persist extends Component +{ + public function render() + { + return view('components.persist'); + } } \ No newline at end of file diff --git a/tests/views/components/entangle.twig b/tests/views/components/entangle.twig index 7bd21ca..51108c3 100644 --- a/tests/views/components/entangle.twig +++ b/tests/views/components/entangle.twig @@ -2,5 +2,5 @@ {% if title %}

{{ title }}

{% endif %} - {% entangle 'entangled' %} + {% livewire.entangle 'entangled' %} \ No newline at end of file diff --git a/tests/views/components/haschilds.twig b/tests/views/components/haschilds.twig index 1c23729..93168f1 100644 --- a/tests/views/components/haschilds.twig +++ b/tests/views/components/haschilds.twig @@ -1,6 +1,6 @@
{{ counter }}
- {% livewire 'childcounter' with { 'count': 1, 'title': 'Child' } %} + {% livewire.component 'childcounter' with { 'count': 1, 'title': 'Child' } %}
diff --git a/tests/views/components/persist.twig b/tests/views/components/persist.twig new file mode 100644 index 0000000..abd6033 --- /dev/null +++ b/tests/views/components/persist.twig @@ -0,0 +1,5 @@ +
+ {% livewire.persist 'player' %} + This element will be kept on page navigation. + {% livewire.endpersist %} +
\ No newline at end of file diff --git a/tests/views/components/table.twig b/tests/views/components/table.twig index 1a21d7a..58eae66 100644 --- a/tests/views/components/table.twig +++ b/tests/views/components/table.twig @@ -1,5 +1,5 @@ {% for element in elements %} - {% livewire 'table.row' with { 'element': element } %} + {% livewire.component 'table.row' with { 'element': element } %} {% endfor %}
diff --git a/tests/views/components/this.twig b/tests/views/components/this.twig index cc3e07a..7455001 100644 --- a/tests/views/components/this.twig +++ b/tests/views/components/this.twig @@ -1 +1,3 @@ - \ No newline at end of file +
+ +
\ No newline at end of file diff --git a/tests/views/entangle-test.twig b/tests/views/entangle-test.twig index e028fba..2f5a70c 100644 --- a/tests/views/entangle-test.twig +++ b/tests/views/entangle-test.twig @@ -1,5 +1,5 @@
{% block content %} - {% livewire 'entangle' %} + {% livewire.component 'entangle' %} {% endblock %}
diff --git a/tests/views/name-type-test.twig b/tests/views/name-type-test.twig index b5b59af..c34856a 100644 --- a/tests/views/name-type-test.twig +++ b/tests/views/name-type-test.twig @@ -1,9 +1,9 @@ {% extends 'layout' %} {% block content %} - {% livewire 'counter' %} + {% livewire.component 'counter' %} - {% livewire 'counter' with {'count': 3} %} + {% livewire.component 'counter' with {'count': 3} %} - {% livewire 'counter' with {'count': 3, 'title': 'Lorem ipsum!'} %} + {% livewire.component 'counter' with {'count': 3, 'title': 'Lorem ipsum!'} %} {% endblock %} diff --git a/tests/views/nested-test.twig b/tests/views/nested-test.twig index e8eb176..0656e55 100644 --- a/tests/views/nested-test.twig +++ b/tests/views/nested-test.twig @@ -6,7 +6,7 @@ ] %} {% block content %} - {% livewire 'dashed-counter' %} + {% livewire.component 'dashed-counter' %} - {% livewire 'table' with {'elements' : elements} %} -{% endblock %} + {% livewire.component 'table' with {'elements' : elements} %} +{% endblock %} \ No newline at end of file diff --git a/tests/views/persist-test.twig b/tests/views/persist-test.twig new file mode 100644 index 0000000..eaf2a36 --- /dev/null +++ b/tests/views/persist-test.twig @@ -0,0 +1,6 @@ +{% extends 'layout' %} + +{% block content %} + {% livewire.component 'persist' %} + {{ livewireScriptConfig({'config': true}) }} +{% endblock %} diff --git a/tests/views/string-type-test.twig b/tests/views/string-type-test.twig index b5b59af..c34856a 100644 --- a/tests/views/string-type-test.twig +++ b/tests/views/string-type-test.twig @@ -1,9 +1,9 @@ {% extends 'layout' %} {% block content %} - {% livewire 'counter' %} + {% livewire.component 'counter' %} - {% livewire 'counter' with {'count': 3} %} + {% livewire.component 'counter' with {'count': 3} %} - {% livewire 'counter' with {'count': 3, 'title': 'Lorem ipsum!'} %} + {% livewire.component 'counter' with {'count': 3, 'title': 'Lorem ipsum!'} %} {% endblock %} diff --git a/tests/views/this-test.twig b/tests/views/this-test.twig index fc7277f..be8fda8 100644 --- a/tests/views/this-test.twig +++ b/tests/views/this-test.twig @@ -1,6 +1,6 @@ {% extends 'layout' %} {% block content %} - {% livewire 'this' %} + {% livewire.component 'this' %} {{ livewireScriptConfig({'config': true}) }} {% endblock %} diff --git a/tests/views/unknown-component-test.twig b/tests/views/unknown-component-test.twig index cb7341c..0b7dddc 100644 --- a/tests/views/unknown-component-test.twig +++ b/tests/views/unknown-component-test.twig @@ -1,5 +1,5 @@ {% extends 'layout' %} {% block content %} - {% livewire 63 %} + {% livewire.component 63 %} {% endblock %} From 7a3f5901e8d1046a2f4c977fc6aed2a0de7af386 Mon Sep 17 00:00:00 2001 From: mbardelmeijer Date: Fri, 22 Sep 2023 17:37:42 +0000 Subject: [PATCH 7/9] Fix styling --- src/LivewireExtension.php | 4 +++- src/LivewireTokenParser.php | 10 ++++++---- src/LivewireTwigServiceProvider.php | 3 --- src/Nodes/EntangleNode.php | 2 +- src/Nodes/LivewireNode.php | 2 +- src/Nodes/ThisNode.php | 2 +- tests/RenderTest.php | 2 +- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index a8891a3..d245e7b 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -4,13 +4,14 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Blade; +use Livewire\Mechanisms\FrontendAssets\FrontendAssets; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -use Livewire\Mechanisms\FrontendAssets\FrontendAssets; class LivewireExtension extends AbstractExtension { protected Collection $calls; + protected array $dirs = [ 'livewireScripts', 'livewireScriptConfig', @@ -24,6 +25,7 @@ public function callDirective(string $directive, array $args = []): string $call = $directives[$directive] ?? null; $r = call_user_func_array($call, $args); + return "?> $r function () use ($stream, $lineno) { $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block + return new ThisNode([], [], $lineno, $this->getTag()); }, @@ -54,14 +55,14 @@ public function parse(Token $token) $stream->expect(Token::BLOCK_END_TYPE); // Parse the body until the 'endpersist' tag. - $body = $this->parser->subparse(function (Token $token) use ($stream) { + $body = $this->parser->subparse(function (Token $token) { return $token->test('livewire'); }, true); // Now, since we know we're at a 'livewire.' token, we should expect the 'endpersist' after it. $stream->expect(Token::PUNCTUATION_TYPE, '.'); - if (!$stream->test(Token::NAME_TYPE, 'endpersist')) { + if (! $stream->test(Token::NAME_TYPE, 'endpersist')) { throw new SyntaxError('Expected the "endpersist" tag.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } @@ -95,10 +96,11 @@ public function parse(Token $token) $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block $attrs = ['variables' => $variables, 'key' => $key]; + return new LivewireNode($component, $attrs, $lineno, $this->getTag()); }, - default => fn() => throw new SyntaxError(sprintf('Unexpected token after "livewire.". Expected %s but got "%s".', implode(' or ', $this->types), $type), $lineno, $stream->getSourceContext()), + default => fn () => throw new SyntaxError(sprintf('Unexpected token after "livewire.". Expected %s but got "%s".', implode(' or ', $this->types), $type), $lineno, $stream->getSourceContext()), })(); } @@ -106,4 +108,4 @@ public function getTag(): string { return 'livewire'; } -} \ No newline at end of file +} diff --git a/src/LivewireTwigServiceProvider.php b/src/LivewireTwigServiceProvider.php index 06d9637..a5146ef 100644 --- a/src/LivewireTwigServiceProvider.php +++ b/src/LivewireTwigServiceProvider.php @@ -2,10 +2,7 @@ namespace Enflow\LivewireTwig; -use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; -use Illuminate\Support\Facades\View; -use Illuminate\Support\Facades\Blade; class LivewireTwigServiceProvider extends ServiceProvider { diff --git a/src/Nodes/EntangleNode.php b/src/Nodes/EntangleNode.php index 16eaec7..b801d0c 100644 --- a/src/Nodes/EntangleNode.php +++ b/src/Nodes/EntangleNode.php @@ -10,7 +10,7 @@ class EntangleNode extends Node { public function compile(Compiler $compiler) { - $livewire = new NameExpression("__livewire", $this->lineno); + $livewire = new NameExpression('__livewire', $this->lineno); $compiler ->write('$__livewire = ')->subcompile($livewire)->raw(";\n") diff --git a/src/Nodes/LivewireNode.php b/src/Nodes/LivewireNode.php index 461dac4..9f98459 100644 --- a/src/Nodes/LivewireNode.php +++ b/src/Nodes/LivewireNode.php @@ -21,7 +21,7 @@ public function compile(Compiler $compiler) $component = $this->getAttribute('component'); $expr = $this->getNode('variables'); $key = $this->getNode('key'); - $hasKey = !$key->hasAttribute('value') || $key->getAttribute('value') !== ''; + $hasKey = ! $key->hasAttribute('value') || $key->getAttribute('value') !== ''; $compiler ->write('$_name = ')->subcompile($component)->raw(";\n") diff --git a/src/Nodes/ThisNode.php b/src/Nodes/ThisNode.php index bb63cf4..daccf87 100644 --- a/src/Nodes/ThisNode.php +++ b/src/Nodes/ThisNode.php @@ -10,7 +10,7 @@ class ThisNode extends Node { public function compile(Compiler $compiler) { - $livewire = new NameExpression("__livewire", $this->lineno); + $livewire = new NameExpression('__livewire', $this->lineno); $compiler ->write('$_instance = ')->subcompile($livewire)->raw(";\n") diff --git a/tests/RenderTest.php b/tests/RenderTest.php index ced9032..6647286 100644 --- a/tests/RenderTest.php +++ b/tests/RenderTest.php @@ -142,4 +142,4 @@ public function render() { return view('components.persist'); } -} \ No newline at end of file +} From 51785d212dcb84df6b46bc1fdcd5cdc13382c5a8 Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Wed, 27 Sep 2023 17:13:45 +0200 Subject: [PATCH 8/9] Cleanup persist token parser, cleanup livewire extension --- src/LivewireExtension.php | 8 -------- src/LivewireTokenParser.php | 12 +++++------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index a8891a3..e579353 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -10,14 +10,6 @@ class LivewireExtension extends AbstractExtension { - protected Collection $calls; - protected array $dirs = [ - 'livewireScripts', - 'livewireScriptConfig', - 'livewireStyles', - 'livewire', - ]; - public function callDirective(string $directive, array $args = []): string { $directives = Blade::getCustomDirectives(); diff --git a/src/LivewireTokenParser.php b/src/LivewireTokenParser.php index bdab058..a2179e9 100644 --- a/src/LivewireTokenParser.php +++ b/src/LivewireTokenParser.php @@ -33,6 +33,7 @@ public function parse(Token $token) // Detect the type after the dot $type = $stream->expect(Token::NAME_TYPE)->getValue(); + // Go into the type specific logic return (match ($type) { 'this' => function () use ($stream, $lineno) { $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block @@ -54,18 +55,15 @@ public function parse(Token $token) $stream->expect(Token::BLOCK_END_TYPE); // Parse the body until the 'endpersist' tag. - $body = $this->parser->subparse(function (Token $token) use ($stream) { - return $token->test('livewire'); - }, true); + $body = $this->parser->subparse(fn(Token $token) => $token->test('livewire'), true); // Now, since we know we're at a 'livewire.' token, we should expect the 'endpersist' after it. $stream->expect(Token::PUNCTUATION_TYPE, '.'); - if (!$stream->test(Token::NAME_TYPE, 'endpersist')) { - throw new SyntaxError('Expected the "endpersist" tag.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); - } + // Now, we should expect the 'endpersist' name. + $stream->expect(Token::NAME_TYPE, 'endpersist'); - $stream->next(); // Consume the 'endpersist' token + // We can expect the closing block now: `%}` $stream->expect(Token::BLOCK_END_TYPE); return new PersistNode([$body], ['name' => $name], $lineno, $this->getTag()); From 702e8896e27882d57ba4e551092b5bd442f8acb7 Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Wed, 27 Sep 2023 17:14:07 +0200 Subject: [PATCH 9/9] Codestyle --- src/LivewireExtension.php | 4 ++-- src/LivewireTokenParser.php | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/LivewireExtension.php b/src/LivewireExtension.php index e579353..2db94ce 100644 --- a/src/LivewireExtension.php +++ b/src/LivewireExtension.php @@ -2,11 +2,10 @@ namespace Enflow\LivewireTwig; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Blade; +use Livewire\Mechanisms\FrontendAssets\FrontendAssets; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -use Livewire\Mechanisms\FrontendAssets\FrontendAssets; class LivewireExtension extends AbstractExtension { @@ -16,6 +15,7 @@ public function callDirective(string $directive, array $args = []): string $call = $directives[$directive] ?? null; $r = call_user_func_array($call, $args); + return "?> $r function () use ($stream, $lineno) { $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block + return new ThisNode([], [], $lineno, $this->getTag()); }, @@ -55,7 +56,7 @@ public function parse(Token $token) $stream->expect(Token::BLOCK_END_TYPE); // Parse the body until the 'endpersist' tag. - $body = $this->parser->subparse(fn(Token $token) => $token->test('livewire'), true); + $body = $this->parser->subparse(fn (Token $token) => $token->test('livewire'), true); // Now, since we know we're at a 'livewire.' token, we should expect the 'endpersist' after it. $stream->expect(Token::PUNCTUATION_TYPE, '.'); @@ -93,10 +94,11 @@ public function parse(Token $token) $stream->expect(Token::BLOCK_END_TYPE); // Expect the end block $attrs = ['variables' => $variables, 'key' => $key]; + return new LivewireNode($component, $attrs, $lineno, $this->getTag()); }, - default => fn() => throw new SyntaxError(sprintf('Unexpected token after "livewire.". Expected %s but got "%s".', implode(' or ', $this->types), $type), $lineno, $stream->getSourceContext()), + default => fn () => throw new SyntaxError(sprintf('Unexpected token after "livewire.". Expected %s but got "%s".', implode(' or ', $this->types), $type), $lineno, $stream->getSourceContext()), })(); } @@ -104,4 +106,4 @@ public function getTag(): string { return 'livewire'; } -} \ No newline at end of file +}