@@ -991,7 +991,74 @@ export const vnode_insertBefore = (
991991 }
992992 }
993993
994- // ensure that the previous node is unlinked.
994+ /**
995+ * Find the parent node and the dom children with the correct namespaces before we unlink the
996+ * previous node. If we don't do this, we will end up with situations where we inflate text nodes
997+ * from shared text node not correctly.
998+ *
999+ * Example:
1000+ *
1001+ * ```
1002+ * <Component>
1003+ * <Projection>a</Projection>
1004+ * <Projection>b</Projection>
1005+ * </Component>
1006+ * ```
1007+ *
1008+ * Projection nodes are virtual nodes, so they don't have a dom parent. They will be written to
1009+ * the q:template element if not visible at the start. Inside the q:template element, the
1010+ * projection nodes will be streamed as single text node "ab". We need to split it, but if we
1011+ * unlink the previous or next sibling, we don't know that after "a" node is "b". So we need to
1012+ * find children first (and inflate them).
1013+ */
1014+ const domParentVNode = vnode_getDomParentVNode ( parent ) ;
1015+ const parentNode = domParentVNode && domParentVNode [ ElementVNodeProps . element ] ;
1016+ let domChildren : ( Element | Text ) [ ] | null = null ;
1017+ if ( domParentVNode ) {
1018+ domChildren = vnode_getDomChildrenWithCorrectNamespacesToInsert (
1019+ journal ,
1020+ domParentVNode ,
1021+ newChild
1022+ ) ;
1023+ }
1024+
1025+ /**
1026+ * Ensure that the previous node is unlinked.
1027+ *
1028+ * We need to do it before finding the adjustedInsertBefore. The problem is when you try to render
1029+ * the same projection multiple times in the same node but under different conditions. We reuse
1030+ * projection nodes, so when this happens, we can end up with a situation where the node is
1031+ * inserted before node above it.
1032+ *
1033+ * Example:
1034+ *
1035+ * ```
1036+ * <>
1037+ * {props.toggle && <Slot />}
1038+ * {!props.toggle && (
1039+ * <>
1040+ * <Slot />
1041+ * </>
1042+ * )}
1043+ * </>
1044+ * ```
1045+ *
1046+ * Projected content:
1047+ *
1048+ * ```
1049+ * <h1>Test</h1>
1050+ * <p>Test content</p>
1051+ * ```
1052+ *
1053+ * If we don't unlink the previous node, we will end up at some point with the following:
1054+ *
1055+ * ```
1056+ * <h1>Test</h1>
1057+ * <p>Test content</p> // <-- inserted before the first h1
1058+ * <h1>Test</h1> // <-- to remove, but still in the tree
1059+ * <p>Test content</p> // <-- to remove
1060+ * ```
1061+ */
9951062 if (
9961063 newChildCurrentParent &&
9971064 ( newChild [ VNodeProps . previousSibling ] ||
@@ -1008,7 +1075,6 @@ export const vnode_insertBefore = (
10081075 // Well, not quite. If the parent is a virtual node, our "last node" is not the same
10091076 // as the DOM "last node". So in that case we need to look for the "next node" from
10101077 // our parent.
1011-
10121078 adjustedInsertBefore = vnode_getDomSibling ( parent , true , false ) ;
10131079 }
10141080 } else if ( vnode_isVirtualVNode ( insertBefore ) ) {
@@ -1018,30 +1084,15 @@ export const vnode_insertBefore = (
10181084 adjustedInsertBefore = insertBefore ;
10191085 }
10201086 adjustedInsertBefore && vnode_ensureInflatedIfText ( journal , adjustedInsertBefore ) ;
1021- // If `insertBefore` is null, than we need to insert at the end of the list.
1022- // Well, not quite. If the parent is a virtual node, our "last node" is not the same
1023- // as the DOM "last node". So in that case we need to look for the "next node" from
1024- // our parent.
1025- // const shouldWeUseParentVirtual = insertBefore == null && vnode_isVirtualVNode(parent);
1026- // const insertBeforeNode = shouldWeUseParentVirtual
1027- // ? vnode_getDomSibling(parent, true)
1028- // : insertBefore;
1029- const domParentVNode = vnode_getDomParentVNode ( parent ) ;
1030- const parentNode = domParentVNode && domParentVNode [ ElementVNodeProps . element ] ;
10311087
1032- if ( parentNode ) {
1033- const domChildren = vnode_getDomChildrenWithCorrectNamespacesToInsert (
1034- journal ,
1035- domParentVNode ,
1036- newChild
1088+ // Here we know the insertBefore node
1089+ if ( domChildren && domChildren . length ) {
1090+ journal . push (
1091+ VNodeJournalOpCode . Insert ,
1092+ parentNode ,
1093+ vnode_getNode ( adjustedInsertBefore ) ,
1094+ ...domChildren
10371095 ) ;
1038- domChildren . length &&
1039- journal . push (
1040- VNodeJournalOpCode . Insert ,
1041- parentNode ,
1042- vnode_getNode ( adjustedInsertBefore ) ,
1043- ...domChildren
1044- ) ;
10451096 }
10461097
10471098 // link newChild into the previous/next list
0 commit comments