From e4ff8adcb9f6b8970f903c846f62e0f973f991f3 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Dec 2024 13:33:03 -0300 Subject: [PATCH 1/6] fix(ssr): missing bookends for slotted lwc:if --- .../src/__tests__/utils/expected-failures.ts | 2 -- .../transformers/component/slotted-content.ts | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts index f0c45d0a3d..d11059eacd 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts @@ -27,8 +27,6 @@ export const expectedFailures = new Set([ 'scoped-slots/mixed-with-light-dom-slots-outside/index.js', 'slot-forwarding/slots/mixed/index.js', 'slot-forwarding/slots/dangling/index.js', - 'slot-not-at-top-level/advanced/lwcIf/light/index.js', - 'slot-not-at-top-level/advanced/lwcIf/shadow/index.js', 'superclass/render-in-superclass/no-template-in-subclass/index.js', 'superclass/render-in-superclass/unused-default-in-subclass/index.js', 'superclass/render-in-superclass/unused-default-in-superclass/index.js', diff --git a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts index 9970bc7d45..dbd6a2f914 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts @@ -75,6 +75,17 @@ const bAddLightContent = esTemplate` }); `; +function getShadowSlottedContent(slottableChildren: IrChildNode[], cxt: TransformerContext) { + return optimizeAdjacentYieldStmts( + slottableChildren.flatMap((child) => { + if (child.type === 'ExternalComponent') { + cxt.isSlotted = false; + } + return irToEs(child, cxt); + }) + ); +} + // Light DOM slots are a bit complex because of needing to handle slots _not_ at the top level // At the non-top level, it matters what the ancestors are. These are relevant to slots: // - If (`if:true`, `if:false`) @@ -153,7 +164,10 @@ function getLightSlottedContent(rootNodes: IrChildNode[], cxt: TransformerContex // '' is the default slot name. Text nodes are always slotted into the default slot const slotName = node.type === 'Text' ? b.literal('') : bAttributeValue(node, 'slot'); + const { isSlotted } = cxt; + cxt.isSlotted = ancestorIndices.length > 0 || node.type === 'Slot'; addLightDomSlotContent(slotName, [...ancestorIndices, i]); + cxt.isSlotted = isSlotted; break; } } @@ -178,7 +192,7 @@ export function getSlottedContent( (child) => child.type === 'ScopedSlotFragment' ) as IrScopedSlotFragment[]; - const shadowSlotContent = optimizeAdjacentYieldStmts(irChildrenToEs(slottableChildren, cxt)); + const shadowSlotContent = getShadowSlottedContent(slottableChildren, cxt); const lightSlotContent = getLightSlottedContent(slottableChildren, cxt); From 4bc97b07e4bc53075ff5d5c8f09c649dc79894e0 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Dec 2024 09:58:20 -0300 Subject: [PATCH 2/6] fix: rename for better readability --- .../transformers/component/slotted-content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts index b05c0cfa19..dbd2d81dbd 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts @@ -166,10 +166,10 @@ function getLightSlottedContent(rootNodes: IrChildNode[], cxt: TransformerContex // '' is the default slot name. Text nodes are always slotted into the default slot const slotName = node.type === 'Text' ? b.literal('') : bAttributeValue(node, 'slot'); - const { isSlotted } = cxt; + const { isSlotted: originalIsSlotted } = cxt; cxt.isSlotted = ancestorIndices.length > 0 || node.type === 'Slot'; addLightDomSlotContent(slotName, [...ancestorIndices, i]); - cxt.isSlotted = isSlotted; + cxt.isSlotted = originalIsSlotted; break; } } From cf95b5cccfc07cc5115832953d9e7c9c305cabba Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Dec 2024 10:12:15 -0300 Subject: [PATCH 3/6] fix: move logic to addLightDomSlotContent --- .../transformers/component/slotted-content.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts index dbd2d81dbd..f9963a520d 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts @@ -141,7 +141,10 @@ function getLightSlottedContent(rootNodes: IrChildNode[], cxt: TransformerContex leaf.attributes = leaf.attributes.filter((attr) => attr.name !== 'slot'); } }); + const { isSlotted: originalIsSlotted } = cxt; + cxt.isSlotted = ancestorIndices.length > 1 || clone.type === 'Slot'; const slotContent = irToEs(clone, cxt); + cxt.isSlotted = originalIsSlotted; results.push(b.expressionStatement(bAddLightContent(slotName, null, slotContent))); }; @@ -166,10 +169,7 @@ function getLightSlottedContent(rootNodes: IrChildNode[], cxt: TransformerContex // '' is the default slot name. Text nodes are always slotted into the default slot const slotName = node.type === 'Text' ? b.literal('') : bAttributeValue(node, 'slot'); - const { isSlotted: originalIsSlotted } = cxt; - cxt.isSlotted = ancestorIndices.length > 0 || node.type === 'Slot'; addLightDomSlotContent(slotName, [...ancestorIndices, i]); - cxt.isSlotted = originalIsSlotted; break; } } From 320116da0e5616877956393f3d9e9a1df53164ec Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 12 Dec 2024 10:39:23 -0300 Subject: [PATCH 4/6] fix: use irChildrenToEs in getShadowSlottedContent --- .../src/compile-template/ir-to-es.ts | 21 +++++++++++++------ .../transformers/component/slotted-content.ts | 3 +-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts b/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts index 2fd1c7c04a..26da440b17 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts @@ -66,14 +66,23 @@ const transformers: Transformers = { Lwc: LwcComponent, }; -export function irChildrenToEs(children: IrChildNode[], cxt: TransformerContext): EsStatement[] { - const result = children.flatMap((child, idx) => { - cxt.prevSibling = children[idx - 1]; - cxt.nextSibling = children[idx + 1]; - return irToEs(child, cxt); - }); +export function irChildrenToEs( + children: IrChildNode[], + cxt: TransformerContext, + cb?: (child: IrChildNode) => void +): EsStatement[] { + const result: EsStatement[] = []; + + for (let i = 0; i < children.length; i++) { + cxt.prevSibling = children[i - 1]; + cxt.nextSibling = children[i + 1]; + cb?.(children[i]); + result.push(...irToEs(children[i], cxt)); + } + cxt.prevSibling = undefined; cxt.nextSibling = undefined; + return result; } diff --git a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts index f9963a520d..16e4e7f1b3 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts @@ -79,11 +79,10 @@ const bAddLightContent = esTemplate` function getShadowSlottedContent(slottableChildren: IrChildNode[], cxt: TransformerContext) { return optimizeAdjacentYieldStmts( - slottableChildren.flatMap((child) => { + irChildrenToEs(slottableChildren, cxt, (child) => { if (child.type === 'ExternalComponent') { cxt.isSlotted = false; } - return irToEs(child, cxt); }) ); } From ddc1dedee2cc42d996edc2e2f7f47ce21613648b Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 17 Dec 2024 09:14:48 -0300 Subject: [PATCH 5/6] fix: slot-not-at-top-level/nested-elements/lwcIf/shadow --- .../ssr-compiler/src/__tests__/utils/expected-failures.ts | 1 - .../@lwc/ssr-compiler/src/compile-template/ir-to-es.ts | 5 +++-- .../transformers/component/slotted-content.ts | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts index 2730fdedc0..25d32f3cd7 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts @@ -26,7 +26,6 @@ export const expectedFailures = new Set([ 'slot-forwarding/slots/mixed/index.js', 'slot-forwarding/slots/dangling/index.js', 'slot-not-at-top-level/lwcIf-with-adjacent-text-nodes/light/index.js', - 'slot-not-at-top-level/nested-elements/lwcIf/shadow/index.js', 'superclass/render-in-superclass/no-template-in-subclass/index.js', 'superclass/render-in-superclass/unused-default-in-subclass/index.js', 'superclass/render-in-superclass/unused-default-in-superclass/index.js', diff --git a/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts b/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts index 26da440b17..a938922933 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts @@ -69,15 +69,16 @@ const transformers: Transformers = { export function irChildrenToEs( children: IrChildNode[], cxt: TransformerContext, - cb?: (child: IrChildNode) => void + cb?: (child: IrChildNode) => (() => void) | void ): EsStatement[] { const result: EsStatement[] = []; for (let i = 0; i < children.length; i++) { cxt.prevSibling = children[i - 1]; cxt.nextSibling = children[i + 1]; - cb?.(children[i]); + const cleanUp = cb?.(children[i]); result.push(...irToEs(children[i], cxt)); + cleanUp?.(); } cxt.prevSibling = undefined; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts index 16e4e7f1b3..b7339f73c1 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts @@ -80,9 +80,15 @@ const bAddLightContent = esTemplate` function getShadowSlottedContent(slottableChildren: IrChildNode[], cxt: TransformerContext) { return optimizeAdjacentYieldStmts( irChildrenToEs(slottableChildren, cxt, (child) => { - if (child.type === 'ExternalComponent') { + const { isSlotted } = cxt; + + if (child.type === 'ExternalComponent' || child.type === 'Element') { cxt.isSlotted = false; } + + return () => { + cxt.isSlotted = isSlotted; + }; }) ); } From 8d67e3f8a3775186efe1636aa2635e2939f80c1e Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 17 Dec 2024 15:47:04 -0300 Subject: [PATCH 6/6] Update packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts Co-authored-by: Nolan Lawson --- .../compile-template/transformers/component/slotted-content.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts index b7339f73c1..62a47db98d 100644 --- a/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts +++ b/packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts @@ -86,6 +86,7 @@ function getShadowSlottedContent(slottableChildren: IrChildNode[], cxt: Transfor cxt.isSlotted = false; } + // cleanup function return () => { cxt.isSlotted = isSlotted; };