Skip to content

Commit 580fc4c

Browse files
committed
feat: curry local variables into the shadow slot content fn
1 parent da23cd0 commit 580fc4c

File tree

3 files changed

+18
-13
lines changed

3 files changed

+18
-13
lines changed

packages/@lwc/ssr-compiler/src/compile-template/context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export function createNewContext(templateOptions: TemplateOpts): {
3333
}
3434
return false;
3535
};
36+
const getLocalVars = () => localVarStack.flatMap((stackFrameVars) => [...stackFrameVars]);
3637

3738
const hoistedStatements = {
3839
module: [] as EsStatement[],
@@ -90,6 +91,7 @@ export function createNewContext(templateOptions: TemplateOpts): {
9091
pushLocalVars,
9192
popLocalVars,
9293
isLocalVar,
94+
getLocalVars,
9395
templateOptions,
9496
hoist,
9597
hoistedStatements,

packages/@lwc/ssr-compiler/src/compile-template/transformers/component/slotted-content.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import type {
2121
Expression as EsExpression,
2222
Statement as EsStatement,
2323
ExpressionStatement as EsExpressionStatement,
24-
FunctionDeclaration as EsFunctionDeclaration,
2524
VariableDeclaration as EsVariableDeclaration,
2625
} from 'estree';
2726
import type {
@@ -40,23 +39,16 @@ import type {
4039
} from '@lwc/template-compiler';
4140
import type { TransformerContext } from '../../types';
4241

43-
// const slotAttributeValueAssignment =
44-
// esTemplate`const slotAttributeValue = null;`<EsVariableDeclaration>();
45-
46-
// Toodles: rather than hoising this function to the top of the module, maybe it should be
47-
// hoisted to the top of the template function. Many things would still be in
48-
// scope that way, and it might not be quite so brittle. And it would still allow
49-
// to dedupe the bullshit
5042
const bGenerateShadowSlottedContent = esTemplateWithYield`
51-
async function* ${/* function name */ is.identifier}(contextfulParent) {
43+
const ${/* function name */ is.identifier} = (${/* local vars */ is.identifier}) => async function* ${/* function name */ 0}(contextfulParent) {
5244
// The 'contextfulParent' variable is shadowed here so that a contextful relationship
5345
// is established between components rendered in slotted content & the "parent"
5446
// component that contains the <slot>.
5547
${/* shadow slot content */ is.statement}
56-
}
57-
`<EsFunctionDeclaration>;
48+
};
49+
`<EsVariableDeclaration>;
5850
const bGenerateShadowSlottedContentRef = esTemplateWithYield`
59-
const shadowSlottedContent = ${/* reference to hoisted fn */ is.identifier};
51+
const shadowSlottedContent = ${/* reference to hoisted fn */ is.identifier}(${/* local vars */ is.identifier});
6052
`<EsVariableDeclaration>;
6153
const bNullishGenerateShadowSlottedContent = esTemplateWithYield`
6254
const shadowSlottedContent = null;
@@ -280,20 +272,30 @@ export function getSlottedContent(
280272
// uniquely identify that node.
281273
const uniqueNodeId = `${node.name}:${node.location.start}:${node.location.end}`;
282274

275+
const localVars = cxt.getLocalVars();
276+
const localVarIds = localVars.map(b.identifier);
277+
283278
if (hasShadowSlottedContent && !cxt.slots.shadow.isDuplicate(uniqueNodeId)) {
284279
// Colon characters in <lwc:component> element name will result in an invalid
285280
// JavaScript identifier if not otherwise accounted for.
286281
const kebabCmpName = kebabCaseToCamelCase(node.name).replace(':', '_');
287282
const shadowSlotContentFnName = cxt.slots.shadow.register(uniqueNodeId, kebabCmpName);
288283
const shadowSlottedContentFn = bGenerateShadowSlottedContent(
289284
b.identifier(shadowSlotContentFnName),
285+
// If the slot-fn were defined here instead of hoisted to the top of the module,
286+
// the local variables (e.g. from for:each) would be closed-over. When hoisted,
287+
// however, we need to curry these variables.
288+
localVarIds,
290289
shadowSlotContent
291290
);
292291
cxt.hoist.templateFn(shadowSlottedContentFn, node);
293292
}
294293

295294
const shadowSlottedContentFn = hasShadowSlottedContent
296-
? bGenerateShadowSlottedContentRef(b.identifier(cxt.slots.shadow.getFnName(uniqueNodeId)!))
295+
? bGenerateShadowSlottedContentRef(
296+
b.identifier(cxt.slots.shadow.getFnName(uniqueNodeId)!),
297+
localVarIds
298+
)
297299
: bNullishGenerateShadowSlottedContent();
298300
const lightSlottedContentMap = hasLightSlottedContent
299301
? bContentMap(b.identifier('lightSlottedContentMap'))

packages/@lwc/ssr-compiler/src/compile-template/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface TransformerContext {
2525
pushLocalVars: (vars: string[]) => void;
2626
popLocalVars: () => void;
2727
isLocalVar: (varName: string | null | undefined) => boolean;
28+
getLocalVars: () => string[];
2829
templateOptions: TemplateOpts;
2930
siblings: IrNode[] | undefined;
3031
currentNodeIndex: number | undefined;

0 commit comments

Comments
 (0)