1515use Paneon \VueToTwig \Models \Property ;
1616use Paneon \VueToTwig \Models \Replacements ;
1717use Paneon \VueToTwig \Models \Slot ;
18+ use Paneon \VueToTwig \Utils \NodeHelper ;
1819use Paneon \VueToTwig \Utils \TwigBuilder ;
1920use Psr \Log \LoggerInterface ;
2021use ReflectionException ;
@@ -52,6 +53,11 @@ class Compiler
5253 */
5354 protected $ builder ;
5455
56+ /**
57+ * @var NodeHelper
58+ */
59+ protected $ nodeHelper ;
60+
5561 /**
5662 * @var Property[]
5763 */
@@ -88,6 +94,7 @@ class Compiler
8894 public function __construct (DOMDocument $ document , LoggerInterface $ logger )
8995 {
9096 $ this ->builder = new TwigBuilder ();
97+ $ this ->nodeHelper = new NodeHelper ();
9198 $ this ->document = $ document ;
9299 $ this ->logger = $ logger ;
93100 $ this ->lastCloseIf = [];
@@ -183,14 +190,16 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
183190 } elseif ($ node instanceof DOMDocument) {
184191 $ this ->logger ->warning ('Document node found. ' );
185192 } elseif ($ node instanceof DOMElement) {
186- $ this ->twigRemove ($ node );
193+ if ($ this ->twigRemove ($ node )) {
194+ return $ node ;
195+ }
187196 $ this ->replaceShowWithIf ($ node );
188197 $ this ->handleIf ($ node , $ level );
189198 $ this ->handleFor ($ node );
190199 $ this ->handleHtml ($ node );
191200 $ this ->handleText ($ node );
192201 $ this ->stripEventHandlers ($ node );
193- $ this ->handleDefaultSlot ($ node );
202+ $ this ->handleSlots ($ node );
194203 $ this ->cleanupAttributes ($ node );
195204 }
196205
@@ -223,21 +232,13 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
223232 $ this ->convertNode ($ childNode , $ level + 1 );
224233 }
225234
226- // Slots (Default)
235+ // Slots
227236 if ($ node ->hasChildNodes ()) {
228- $ innerHtml = $ this ->innerHtmlOfNode ($ node );
229- $ innerHtml = $ this ->replacePlaceholders ($ innerHtml );
230- $ this ->logger ->debug (
231- 'Add default slot: ' ,
232- [
233- 'nodeValue ' => $ node ->nodeValue ,
234- 'innerHtml ' => $ innerHtml ,
235- ]
236- );
237-
238- $ slot = $ usedComponent ->addDefaultSlot ($ innerHtml );
239-
240- $ this ->addReplaceVariable ($ slot ->getSlotContentVariableString (), $ slot ->getValue ());
237+ $ this ->handleNamedSlotsInclude ($ node , $ usedComponent );
238+ // Slots (Default)
239+ if ($ node ->hasChildNodes () && !$ usedComponent ->hasSlot (Slot::SLOT_DEFAULT_NAME )) {
240+ $ this ->addSlot (Slot::SLOT_DEFAULT_NAME , $ node , $ usedComponent );
241+ }
241242 }
242243
243244 // Include Partial
@@ -271,7 +272,7 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
271272 }
272273
273274 // Remove original node
274- $ node -> parentNode -> removeChild ($ node );
275+ $ this -> nodeHelper -> removeNode ($ node );
275276
276277 return $ node ;
277278 }
@@ -479,7 +480,7 @@ public function handleBinding(string $value, string $name, ?DOMElement $node = n
479480
480481 foreach ($ items as $ item ) {
481482 if (preg_match ($ regexObjectElements , $ item , $ matchElement )) {
482- $ dynamicValues [] = $ this ->prepareBindingOutput (
483+ $ dynamicValues [] = $ this ->builder -> prepareBindingOutput (
483484 $ this ->builder ->refactorCondition ($ matchElement ['condition ' ]) . ' ? \'' . $ matchElement ['class ' ] . ' \'' ,
484485 $ twigOutput
485486 );
@@ -493,7 +494,7 @@ public function handleBinding(string $value, string $name, ?DOMElement $node = n
493494 foreach ($ matches as $ match ) {
494495 $ templateStringContent = str_replace (
495496 $ match [0 ],
496- $ this ->prepareBindingOutput ($ this ->builder ->refactorCondition ($ match [1 ]), $ twigOutput ),
497+ $ this ->builder -> prepareBindingOutput ($ this ->builder ->refactorCondition ($ match [1 ]), $ twigOutput ),
497498 $ templateStringContent
498499 );
499500 }
@@ -502,25 +503,12 @@ public function handleBinding(string $value, string $name, ?DOMElement $node = n
502503 } else {
503504 $ value = $ this ->builder ->refactorCondition ($ value );
504505 $ this ->logger ->debug (sprintf ('- setAttribute "%s" with value "%s" ' , $ name , $ value ));
505- $ dynamicValues [] = $ this ->prepareBindingOutput ($ value , $ twigOutput );
506+ $ dynamicValues [] = $ this ->builder -> prepareBindingOutput ($ value , $ twigOutput );
506507 }
507508
508509 return $ dynamicValues ;
509510 }
510511
511- private function prepareBindingOutput (string $ value , bool $ twigOutput = true ): string
512- {
513- $ open = Replacements::getSanitizedConstant ('DOUBLE_CURLY_OPEN ' );
514- $ close = Replacements::getSanitizedConstant ('DOUBLE_CURLY_CLOSE ' );
515-
516- if (!$ twigOutput ) {
517- $ open = '( ' ;
518- $ close = ') ' ;
519- }
520-
521- return $ open . ' ' . $ value . ' ' . $ close ;
522- }
523-
524512 /**
525513 * @throws ReflectionException
526514 */
@@ -539,7 +527,7 @@ private function cleanupAttributes(DOMElement $node): void
539527 /** @var DOMAttr $attribute */
540528 foreach ($ node ->attributes as $ attribute ) {
541529 if (
542- (preg_match ('/^v-([a-z]*)/ ' , $ attribute ->name , $ matches ) === 1 && $ matches [1 ] !== 'bind ' )
530+ (preg_match ('/^v-([a-z]*)/ ' , $ attribute ->name , $ matches ) === 1 && $ matches [1 ] !== 'bind ' && $ matches [ 1 ] !== ' slot ' )
543531 || preg_match ('/^[:]?ref$/ ' , $ attribute ->name ) === 1
544532 ) {
545533 $ removeAttributes [] = $ attribute ->name ;
@@ -692,25 +680,6 @@ private function handleText(DOMElement $node): void
692680 $ node ->appendChild (new DOMText ('{{ ' . $ text . '}} ' ));
693681 }
694682
695- protected function addDefaultsToVariable (string $ varName , string $ string ): string
696- {
697- if (!in_array ($ varName , array_keys ($ this ->properties ))) {
698- return $ string ;
699- }
700-
701- $ prop = $ this ->properties [$ varName ];
702-
703- if ($ prop ->hasDefault ()) {
704- $ string = preg_replace (
705- '/\b( ' . $ varName . ')\b/ ' ,
706- $ varName . '|default( ' . $ prop ->getDefault () . ') ' ,
707- $ string
708- );
709- }
710-
711- return $ string ;
712- }
713-
714683 /**
715684 * @throws RuntimeException
716685 */
@@ -907,28 +876,68 @@ protected function addVariableBlocks(string $string): string
907876 /**
908877 * @throws Exception
909878 */
910- protected function handleDefaultSlot (DOMElement $ node ): void
879+ protected function handleSlots (DOMElement $ node ): void
911880 {
912881 if ($ node ->nodeName !== 'slot ' ) {
913882 return ;
914883 }
915884
916885 $ slotFallback = $ node ->hasChildNodes () ? $ this ->innerHtmlOfNode ($ node ) : null ;
917886
887+ $ slotName = Slot::SLOT_PREFIX ;
888+ $ slotName .= $ node ->getAttribute ('name ' ) ? $ node ->getAttribute ('name ' ) : Slot::SLOT_DEFAULT_NAME ;
889+
918890 if ($ slotFallback ) {
919- $ this ->addVariable ('slot_default_fallback ' , $ slotFallback );
920- $ variable = $ this ->builder ->createVariableOutput (
921- Slot::SLOT_PREFIX . Slot::SLOT_DEFAULT_NAME ,
922- 'slot_default_fallback '
923- );
891+ $ this ->addVariable ($ slotName . '_fallback ' , $ slotFallback );
892+ $ variable = $ this ->builder ->createVariableOutput ($ slotName , $ slotName . '_fallback ' );
924893 } else {
925- $ variable = $ this ->builder ->createVariableOutput (Slot:: SLOT_PREFIX . Slot:: SLOT_DEFAULT_NAME );
894+ $ variable = $ this ->builder ->createVariableOutput ($ slotName );
926895 }
927896
928897 $ variableNode = $ this ->document ->createTextNode ($ variable );
929898
930899 $ node ->parentNode ->insertBefore ($ variableNode , $ node );
931- $ node ->parentNode ->removeChild ($ node );
900+ $ this ->nodeHelper ->removeNode ($ node );
901+ }
902+
903+ /**
904+ * @throws Exception
905+ * @throws ReflectionException
906+ */
907+ protected function handleNamedSlotsInclude (DOMNode $ node , Component $ usedComponent ): void
908+ {
909+ $ removeNodes = [];
910+ foreach ($ node ->childNodes as $ childNode ) {
911+ if ($ childNode instanceof DOMElement && $ childNode ->tagName === 'template ' ) {
912+ foreach ($ childNode ->attributes as $ attribute ) {
913+ if ($ attribute instanceof DOMAttr && preg_match ('/v-slot(?::([a-z]+)?)/i ' , $ attribute ->nodeName , $ matches )) {
914+ $ slotName = $ matches [1 ] ?? Slot::SLOT_DEFAULT_NAME ;
915+ $ this ->addSlot ($ slotName , $ childNode , $ usedComponent );
916+ $ removeNodes [] = $ childNode ;
917+ }
918+ }
919+ }
920+ }
921+ $ this ->nodeHelper ->removeNodes ($ removeNodes );
922+ }
923+
924+ /**
925+ * @throws Exception
926+ * @throws ReflectionException
927+ */
928+ protected function addSlot (string $ slotName , DOMNode $ node , Component $ usedComponent ): void
929+ {
930+ $ innerHtml = $ this ->replacePlaceholders ($ this ->innerHtmlOfNode ($ node ));
931+ $ this ->logger ->debug (
932+ 'Add ' . $ slotName . ' slot: ' ,
933+ [
934+ 'nodeValue ' => $ node ->nodeValue ,
935+ 'innerHtml ' => $ innerHtml ,
936+ ]
937+ );
938+
939+ $ slot = $ usedComponent ->addSlot ($ slotName , $ innerHtml );
940+ $ this ->addReplaceVariable ($ slot ->getSlotContentVariableString (), $ slot ->getValue ());
932941 }
933942
934943 protected function insertDefaultValues (): void
@@ -949,7 +958,7 @@ protected function handleRootNodeAttribute(DOMElement $node, ?string $name = nul
949958 if (!$ name ) {
950959 return $ node ;
951960 }
952- $ string = $ this ->prepareBindingOutput ($ name . '|default( \'\') ' );
961+ $ string = $ this ->builder -> prepareBindingOutput ($ name . '|default( \'\') ' );
953962 if ($ node ->hasAttribute ($ name )) {
954963 $ attribute = $ node ->getAttributeNode ($ name );
955964 $ attribute ->value .= ' ' . $ string ;
@@ -965,14 +974,18 @@ private function handleCommentNode(DOMComment $node): void
965974 {
966975 $ nodeValue = trim ($ node ->nodeValue );
967976 if (preg_match ('/^(eslint-disable|@?todo)/i ' , $ nodeValue ) === 1 ) {
968- $ node -> parentNode -> removeChild ($ node );
977+ $ this -> nodeHelper -> removeNode ($ node );
969978 }
970979 }
971980
972- private function twigRemove (DOMElement $ node ): void
981+ private function twigRemove (DOMElement $ node ): bool
973982 {
974983 if ($ node ->hasAttribute ('data-twig-remove ' )) {
975984 $ node ->parentNode ->removeChild ($ node );
985+
986+ return true ;
976987 }
988+
989+ return false ;
977990 }
978991}
0 commit comments