Skip to content

Commit 59b8f78

Browse files
committed
HTML syntaxt to feat with slot
1 parent 1ef23b8 commit 59b8f78

File tree

2 files changed

+43
-83
lines changed

2 files changed

+43
-83
lines changed

src/TwigComponent/src/Twig/TwigPreLexer.php

Lines changed: 16 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -57,37 +57,19 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
5757
if ($isTwigHtmlOpening || (0 !== \count($this->currentComponents) && $isTraditionalBlockOpening = $this->consume('{% block'))) {
5858
$componentName = $isTraditionalBlockOpening ? 'block' : $this->consumeComponentName();
5959

60-
if ('block' === $componentName) {
61-
// if we're already inside the "default" block, let's close it
62-
if (!empty($this->currentComponents) && $this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']) {
63-
$output .= '{% endblock %}';
60+
if ($isTraditionalBlockOpening) {
61+
$output .= '{% '.$componentName;
62+
$output .= $this->consumeUntil('%}');
6463

65-
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = false;
66-
}
67-
68-
if ($isTraditionalBlockOpening) {
69-
// add what we've consumed so far
70-
$output .= '{% block';
71-
$output .= $this->consumeUntil('%}');
72-
$output .= $this->consumeUntilEndBlock();
73-
74-
continue;
75-
}
64+
continue;
65+
}
7666

67+
if ('slot' === $componentName) {
7768
$output .= $this->consumeBlock($componentName);
7869

7970
continue;
8071
}
8172

82-
// if we're already inside a component, and we're not inside a block,
83-
// *and* we've just found a new component, then we should try to
84-
// open the default block
85-
if (!$insideOfBlock
86-
&& !empty($this->currentComponents)
87-
&& !$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']) {
88-
$output .= $this->addDefaultBlock();
89-
}
90-
9173
$attributes = $this->consumeAttributes($componentName);
9274
$isSelfClosing = $this->consume('/>');
9375
if (!$isSelfClosing) {
@@ -99,9 +81,9 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
9981
// use the simpler component() format, so that the system doesn't think
10082
// this is an "embedded" component with blocks
10183
// see https://github.com/symfony/ux/issues/810
102-
$output .= "{{ component('{$componentName}'".($attributes ? ", { {$attributes} }" : '').') }}';
84+
$output .= "{% twig_component '{$componentName}'".($attributes ? " with { {$attributes} }" : '').' %}{% end_twig_component %}';
10385
} else {
104-
$output .= "{% component '{$componentName}'".($attributes ? " with { {$attributes} }" : '').' %}';
86+
$output .= "{% twig_component '{$componentName}'".($attributes ? " with { {$attributes} }" : '').' %}';
10587
}
10688

10789
continue;
@@ -119,13 +101,7 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
119101
throw new SyntaxError("Expected closing tag '</twig:{$lastComponentName}>' but found '</twig:{$closingComponentName}>'", $this->line);
120102
}
121103

122-
// we've reached the end of this component. If we're inside the
123-
// default block, let's close it
124-
if ($lastComponent['hasDefaultBlock']) {
125-
$output .= '{% endblock %}';
126-
}
127-
128-
$output .= '{% endcomponent %}';
104+
$output .= '{% end_twig_component %}';
129105

130106
continue;
131107
}
@@ -135,15 +111,6 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
135111
++$this->line;
136112
}
137113

138-
// handle adding a default block if we find non-whitespace outside of a block
139-
if (!empty($this->currentComponents)
140-
&& !$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']
141-
&& preg_match('/\S/', $char)
142-
&& !$this->check('{% block')
143-
) {
144-
$output .= $this->addDefaultBlock();
145-
}
146-
147114
$output .= $char;
148115
$this->consumeChar();
149116
}
@@ -356,9 +323,9 @@ private function consumeBlock(string $componentName): string
356323
throw new SyntaxError('Expected block name.', $this->line);
357324
}
358325

359-
$output = "{% block {$blockName} %}";
326+
$output = "{% slot {$blockName} %}";
360327

361-
$closingTag = '</twig:block>';
328+
$closingTag = '</twig:slot>';
362329
if (!$this->doesStringEventuallyExist($closingTag)) {
363330
throw new SyntaxError("Expected closing tag '{$closingTag}' for block '{$blockName}'.", $this->line);
364331
}
@@ -368,7 +335,7 @@ private function consumeBlock(string $componentName): string
368335
$output .= $subLexer->preLexComponents($blockContents, true);
369336

370337
$this->consume($closingTag);
371-
$output .= '{% endblock %}';
338+
$output .= '{% endslot %}';
372339

373340
return $output;
374341
}
@@ -379,15 +346,15 @@ private function consumeUntilEndBlock(): string
379346

380347
$depth = 1;
381348
while ($this->position < $this->length) {
382-
if ('</twig:block' === substr($this->input, $this->position, 12)) {
349+
if ('</twig:slot' === substr($this->input, $this->position, 11)) {
383350
if (1 === $depth) {
384351
break;
385352
} else {
386353
--$depth;
387354
}
388355
}
389356

390-
if ('{% endblock %}' === substr($this->input, $this->position, 14)) {
357+
if ('{% endslot %}' === substr($this->input, $this->position, 13)) {
391358
if (1 === $depth) {
392359
// in this case, we want to advanced ALL the way beyond the endblock
393360
$this->position += 14;
@@ -397,11 +364,11 @@ private function consumeUntilEndBlock(): string
397364
}
398365
}
399366

400-
if ('<twig:block' === substr($this->input, $this->position, 11)) {
367+
if ('<twig:slot' === substr($this->input, $this->position, 10)) {
401368
++$depth;
402369
}
403370

404-
if ('{% block' === substr($this->input, $this->position, 8)) {
371+
if ('{% slot' === substr($this->input, $this->position, 7)) {
405372
++$depth;
406373
}
407374

@@ -461,11 +428,4 @@ private function doesStringEventuallyExist(string $needle): bool
461428

462429
return str_contains($remainingString, $needle);
463430
}
464-
465-
private function addDefaultBlock(): string
466-
{
467-
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = true;
468-
469-
return '{% block content %}';
470-
}
471431
}

src/TwigComponent/tests/Unit/TwigPreLexerTest.php

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@ public function getLexTests(): iterable
2929
{
3030
yield 'simple_component' => [
3131
'<twig:foo />',
32-
'{{ component(\'foo\') }}',
32+
'{% twig_component \'foo\' %}{% end_twig_component %}',
3333
];
3434

3535
yield 'component_with_attributes' => [
3636
'<twig:foo bar="baz" with_quotes="It\'s with quotes" />',
37-
"{{ component('foo', { bar: 'baz', with_quotes: 'It\'s with quotes' }) }}",
37+
"{% twig_component 'foo' with { bar: 'baz', with_quotes: 'It\'s with quotes' } %}{% end_twig_component %}",
3838
];
3939

4040
yield 'component_with_dynamic_attributes' => [
4141
'<twig:foo dynamic="{{ dynamicVar }}" :otherDynamic="anotherVar" />',
42-
'{{ component(\'foo\', { dynamic: (dynamicVar), otherDynamic: anotherVar }) }}',
42+
'{% twig_component \'foo\' with { dynamic: (dynamicVar), otherDynamic: anotherVar } %}{% end_twig_component %}',
4343
];
4444

4545
yield 'component_with_closing_tag' => [
@@ -54,27 +54,27 @@ public function getLexTests(): iterable
5454

5555
yield 'component_with_traditional_block' => [
5656
'<twig:foo>{% block foo_block %}Foo{% endblock %}</twig:foo>',
57-
'{% component \'foo\' %}{% block foo_block %}Foo{% endblock %}{% endcomponent %}',
57+
'{% twig_component \'foo\' %}{% block foo_block %}Foo{% endblock %}{% end_twig_component %}',
5858
];
5959

6060
yield 'traditional_blocks_around_component_do_not_confuse' => [
6161
'Hello {% block foo_block %}Foo{% endblock %}<twig:foo />{% block bar_block %}Bar{% endblock %}',
62-
'Hello {% block foo_block %}Foo{% endblock %}{{ component(\'foo\') }}{% block bar_block %}Bar{% endblock %}',
62+
'Hello {% block foo_block %}Foo{% endblock %}{% twig_component \'foo\' %}{% end_twig_component %}{% block bar_block %}Bar{% endblock %}',
6363
];
6464

6565
yield 'component_with_embedded_component_inside_block' => [
6666
'<twig:foo><twig:slot name="foo_block"><twig:bar /></twig:slot></twig:foo>',
67-
'{% twig_component \'foo\' %}{% slot foo_block %}{{ component(\'bar\') }}{% endslot %}{% end_twig_component %}',
67+
'{% twig_component \'foo\' %}{% slot foo_block %}{% twig_component \'bar\' %}{% end_twig_component %}{% endslot %}{% end_twig_component %}',
6868
];
6969

7070
yield 'attribute_with_no_value' => [
7171
'<twig:foo bar />',
72-
'{{ component(\'foo\', { bar: true }) }}',
72+
'{% twig_component \'foo\' with { bar: true } %}{% end_twig_component %}',
7373
];
7474

7575
yield 'attribute_with_no_value_and_no_attributes' => [
7676
'<twig:foo/>',
77-
'{{ component(\'foo\') }}',
77+
'{% twig_component \'foo\' %}{% end_twig_component %}',
7878
];
7979

8080
yield 'component_with_default_block_content' => [
@@ -84,7 +84,7 @@ public function getLexTests(): iterable
8484

8585
yield 'component_with_default_block_that_holds_a_component_and_multi_blocks' => [
8686
'<twig:foo>Foo <twig:bar /><twig:slot name="other_block">Other block</twig:slot></twig:foo>',
87-
'{% twig_component \'foo\' %}Foo {{ component(\'bar\') }}{% slot other_block %}Other block{% endslot %}{% end_twig_component %}',
87+
'{% twig_component \'foo\' %}Foo {% twig_component \'bar\' %}{% end_twig_component %}{% slot other_block %}Other block{% endslot %}{% end_twig_component %}',
8888
];
8989
yield 'component_with_character_:_on_his_name' => [
9090
'<twig:foo:bar></twig:foo:bar>',
@@ -108,19 +108,19 @@ public function getLexTests(): iterable
108108
];
109109
yield 'component_with_mixture_of_string_and_twig_in_argument' => [
110110
'<twig:foo text="Hello {{ name }}!"/>',
111-
"{{ component('foo', { text: 'Hello '~(name)~'!' }) }}",
111+
"{% twig_component 'foo' with { text: 'Hello '~(name)~'!' } %}{% end_twig_component %}",
112112
];
113113
yield 'component_with_mixture_of_dynamic_twig_from_start' => [
114114
'<twig:foo text="{{ name }} is my name{{ ending~\'!!\' }}"/>',
115-
"{{ component('foo', { text: (name)~' is my name'~(ending~'!!') }) }}",
115+
"{% twig_component 'foo' with { text: (name)~' is my name'~(ending~'!!') } %}{% end_twig_component %}",
116116
];
117117
yield 'dynamic_attribute_with_quotation_included' => [
118118
'<twig:foo text="{{ "hello!" }}"/>',
119-
"{{ component('foo', { text: (\"hello!\") }) }}",
119+
"{% twig_component 'foo' with { text: (\"hello!\") } %}{% end_twig_component %}",
120120
];
121121
yield 'component_with_mixture_of_string_and_twig_with_quote_in_argument' => [
122122
'<twig:foo text="Hello {{ name }}, I\'m Theo!"/>',
123-
"{{ component('foo', { text: 'Hello '~(name)~', I\'m Theo!' }) }}",
123+
"{% twig_component 'foo' with { text: 'Hello '~(name)~', I\'m Theo!' } %}{% end_twig_component %}",
124124
];
125125
yield 'component_where_entire_default_block_is_embedded_component' => [
126126
<<<EOF
@@ -129,9 +129,9 @@ public function getLexTests(): iterable
129129
</twig:foo>
130130
EOF,
131131
<<<EOF
132-
{% component 'foo' %}
133-
{% block content %}{% component 'bar' %}{% block content %}bar content{% endblock %}{% endcomponent %}
134-
{% endblock %}{% endcomponent %}
132+
{% twig_component 'foo' %}
133+
{% twig_component 'bar' %}bar content{% end_twig_component %}
134+
{% end_twig_component %}
135135
EOF
136136
];
137137
yield 'component_where_entire_default_block_is_embedded_component_self_closing' => [
@@ -141,9 +141,9 @@ public function getLexTests(): iterable
141141
</twig:foo>
142142
EOF,
143143
<<<EOF
144-
{% component 'foo' %}
145-
{% block content %}{{ component('bar') }}
146-
{% endblock %}{% endcomponent %}
144+
{% twig_component 'foo' %}
145+
{% twig_component 'bar' %}{% end_twig_component %}
146+
{% end_twig_component %}
147147
EOF
148148
];
149149

@@ -155,36 +155,36 @@ public function getLexTests(): iterable
155155
]" />
156156
EOF,
157157
<<<EOF
158-
{{ component('TabbedCodeBlocks', { files: [
158+
{% twig_component 'TabbedCodeBlocks' with { files: [
159159
'src/Twig/MealPlanner.php',
160160
'templates/components/MealPlanner.html.twig',
161-
] }) }}
161+
] } %}{% end_twig_component %}
162162
EOF
163163
];
164164

165165
yield 'component_with_dashed_attribute' => [
166166
'<twig:foobar data-action="foo#bar"></twig:foobar>',
167-
'{% component \'foobar\' with { \'data-action\': \'foo#bar\' } %}{% endcomponent %}',
167+
'{% twig_component \'foobar\' with { \'data-action\': \'foo#bar\' } %}{% end_twig_component %}',
168168
];
169169

170170
yield 'component_with_dashed_attribute_self_closing' => [
171171
'<twig:foobar data-action="foo#bar" />',
172-
'{{ component(\'foobar\', { \'data-action\': \'foo#bar\' }) }}',
172+
'{% twig_component \'foobar\' with { \'data-action\': \'foo#bar\' } %}{% end_twig_component %}',
173173
];
174174

175175
yield 'component_with_colon_attribute' => [
176176
'<twig:foobar my:attribute="yo"></twig:foobar>',
177-
'{% component \'foobar\' with { \'my:attribute\': \'yo\' } %}{% endcomponent %}',
177+
'{% twig_component \'foobar\' with { \'my:attribute\': \'yo\' } %}{% end_twig_component %}',
178178
];
179179

180180
yield 'component_with_truthy_attribute' => [
181181
'<twig:foobar data-turbo-stream></twig:foobar>',
182-
'{% component \'foobar\' with { \'data-turbo-stream\': true } %}{% endcomponent %}',
182+
'{% twig_component \'foobar\' with { \'data-turbo-stream\': true } %}{% end_twig_component %}',
183183
];
184184

185185
yield 'ignore_twig_comment' => [
186186
'{# <twig:Alert/> #} <twig:Alert/>',
187-
'{# <twig:Alert/> #} {{ component(\'Alert\') }}',
187+
'{# <twig:Alert/> #} {% twig_component \'Alert\' %}{% end_twig_component %}',
188188
];
189189

190190
yield 'file_ended_with_comments' => [
@@ -194,7 +194,7 @@ public function getLexTests(): iterable
194194

195195
yield 'mixing_component_and_file_ended_with_comments' => [
196196
'<twig:Alert/> {# <twig:Alert/> #}',
197-
'{{ component(\'Alert\') }} {# <twig:Alert/> #}',
197+
'{% twig_component \'Alert\' %}{% end_twig_component %} {# <twig:Alert/> #}',
198198
];
199199
}
200200
}

0 commit comments

Comments
 (0)