-
Notifications
You must be signed in to change notification settings - Fork 439
Expand file tree
/
Copy pathslot.ts
More file actions
111 lines (102 loc) · 4.92 KB
/
slot.ts
File metadata and controls
111 lines (102 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { is, builders as b } from 'estree-toolkit';
import { esTemplateWithYield } from '../../estemplate';
import { irChildrenToEs } from '../ir-to-es';
import { bAttributeValue, getScopedExpression } from '../shared';
import { isNullableOf } from '../../estree/validators';
import { Element } from './element';
import type { Slot as IrSlot } from '@lwc/template-compiler';
import type {
Statement as EsStatement,
IfStatement as EsIfStatement,
Expression as EsExpression,
} from 'estree';
import type { Transformer } from '../types';
const bConditionalSlot = esTemplateWithYield`
if (isLightDom) {
const isScopedSlot = ${/* isScopedSlot */ is.literal};
const isSlotted = ${/* isSlotted */ is.literal};
const slotName = ${/* slotName */ is.expression};
const lightGenerators = lightSlottedContent?.[slotName ?? ""];
const scopedGenerators = scopedSlottedContent?.[slotName ?? ""];
const mismatchedSlots = isScopedSlot ? lightGenerators : scopedGenerators;
const generators = isScopedSlot ? scopedGenerators : lightGenerators;
/*
If a slotAttributeValue is present, it should be provided for assignment to any slotted content. This behavior aligns with v1 and engine-dom.
See: engine-server/src/__tests__/fixtures/slot-forwarding/slots/dangling/ for example.
Note the slot mapping does not work for scoped slots, so the slot name is not rendered in this case.
See: engine-server/src/__tests__/fixtures/slot-forwarding/scoped-slots for example.
*/
const danglingSlotName = !isScopedSlot ? ${/* slotAttributeValue */ is.expression} || slotAttributeValue : null;
// start bookend HTML comment for light DOM slot vfragment
if (!isSlotted) {
yield '<!---->';
// If there is slot data, scoped slot factory has its own vfragment hence its own bookend
if (isScopedSlot && generators) {
yield '<!---->';
}
}
if (generators) {
for (let i = 0; i < generators.length; i++) {
yield* generators[i](contextfulParent, ${/* scoped slot data */ isNullableOf(is.expression)}, danglingSlotName);
// Scoped slotted data is separated by bookends. Final bookends are added outside of the loop below.
if (isScopedSlot && i < generators.length - 1) {
yield '<!---->';
yield '<!---->';
}
}
/*
If there were mismatched slots, do not fallback to the default. This is required for parity with
engine-core which resets children to an empty array when there are children (mismatched or not).
Because the child nodes are reset, the default slotted content is not rendered in the mismatched slot case.
See https://github.com/salesforce/lwc/blob/master/packages/%40lwc/engine-core/src/framework/api.ts#L238
*/
} else if (!mismatchedSlots) {
// If we're in this else block, then the generator _must_ have yielded
// something. It's impossible for a slottedContent["foo"] to exist
// without the generator yielding at least a text node / element.
// FIXME: how does this work with comments and lwc:preserve-comments?
// TODO: default/fallback slot content
${/* slot fallback content */ is.statement}
}
// end bookend HTML comment for light DOM slot vfragment
if (!isSlotted) {
yield '<!---->';
// If there is slot data, scoped slot factory has its own vfragment hence its own bookend
if (isScopedSlot && generators) {
yield '<!---->';
}
}
} else {
${/* slot element AST */ is.statement}
}
`<EsIfStatement>;
export const Slot: Transformer<IrSlot> = function Slot(node, ctx): EsStatement[] {
const slotBindDirective = node.directives.find((dir) => dir.name === 'SlotBind');
const slotBound = slotBindDirective?.value
? getScopedExpression(slotBindDirective.value as EsExpression, ctx)
: null;
const slotName = bAttributeValue(node, 'name');
// FIXME: avoid serializing the slot's children twice
const slotAst = Element(node, ctx);
const slotChildren = irChildrenToEs(node.children, ctx);
const isScopedSlot = b.literal(Boolean(slotBound));
const isSlotted = b.literal(Boolean(ctx.isSlotted));
const slotAttributeValue = bAttributeValue(node, 'slot');
return [
bConditionalSlot(
isScopedSlot,
isSlotted,
slotName,
slotAttributeValue,
slotBound,
slotChildren,
slotAst
),
];
};