1
- /** @import { BlockStatement, Expression, ExpressionStatement , Property, Statement } from 'estree' */
2
- /** @import { Attribute, Component, SvelteComponent, SvelteSelf, TemplateNode, Text } from '#compiler' */
1
+ /** @import { BlockStatement, Expression, Pattern , Property, Statement } from 'estree' */
2
+ /** @import { Attribute, Component, LetDirective, SvelteComponent, SvelteSelf, TemplateNode, Text } from '#compiler' */
3
3
/** @import { ComponentContext } from '../../types.js' */
4
4
import { empty_comment , serialize_attribute_value } from './utils.js' ;
5
5
import * as b from '../../../../../utils/builders.js' ;
@@ -17,8 +17,8 @@ export function serialize_inline_component(node, expression, context) {
17
17
/** @type {Property[] } */
18
18
const custom_css_props = [ ] ;
19
19
20
- /** @type {ExpressionStatement[] } */
21
- const lets = [ ] ;
20
+ /** @type {Record<string, LetDirective[]> } */
21
+ const lets = { default : [ ] } ;
22
22
23
23
/** @type {Record<string, TemplateNode[]> } */
24
24
const children = { } ;
@@ -27,7 +27,9 @@ export function serialize_inline_component(node, expression, context) {
27
27
* If this component has a slot property, it is a named slot within another component. In this case
28
28
* the slot scope applies to the component itself, too, and not just its children.
29
29
*/
30
- let slot_scope_applies_to_itself = false ;
30
+ const slot_scope_applies_to_itself = node . attributes . some (
31
+ ( node ) => node . type === 'Attribute' && node . name === 'slot'
32
+ ) ;
31
33
32
34
/**
33
35
* Components may have a children prop and also have child nodes. In this case, we assume
@@ -50,7 +52,9 @@ export function serialize_inline_component(node, expression, context) {
50
52
}
51
53
for ( const attribute of node . attributes ) {
52
54
if ( attribute . type === 'LetDirective' ) {
53
- lets . push ( /** @type {ExpressionStatement } */ ( context . visit ( attribute ) ) ) ;
55
+ if ( ! slot_scope_applies_to_itself ) {
56
+ lets . default . push ( attribute ) ;
57
+ }
54
58
} else if ( attribute . type === 'SpreadAttribute' ) {
55
59
props_and_spreads . push ( /** @type {Expression } */ ( context . visit ( attribute ) ) ) ;
56
60
} else if ( attribute . type === 'Attribute' ) {
@@ -60,10 +64,6 @@ export function serialize_inline_component(node, expression, context) {
60
64
continue ;
61
65
}
62
66
63
- if ( attribute . name === 'slot' ) {
64
- slot_scope_applies_to_itself = true ;
65
- }
66
-
67
67
if ( attribute . name === 'children' ) {
68
68
has_children_prop = true ;
69
69
}
@@ -90,10 +90,6 @@ export function serialize_inline_component(node, expression, context) {
90
90
}
91
91
}
92
92
93
- if ( slot_scope_applies_to_itself ) {
94
- context . state . init . push ( ...lets ) ;
95
- }
96
-
97
93
/** @type {Statement[] } */
98
94
const snippet_declarations = [ ] ;
99
95
@@ -115,13 +111,20 @@ export function serialize_inline_component(node, expression, context) {
115
111
116
112
let slot_name = 'default' ;
117
113
if ( is_element_node ( child ) ) {
118
- const attribute = /** @type {Attribute | undefined } */ (
114
+ const slot = /** @type {Attribute | undefined } */ (
119
115
child . attributes . find (
120
116
( attribute ) => attribute . type === 'Attribute' && attribute . name === 'slot'
121
117
)
122
118
) ;
123
- if ( attribute !== undefined ) {
124
- slot_name = /** @type {Text[] } */ ( attribute . value ) [ 0 ] . data ;
119
+
120
+ if ( slot !== undefined ) {
121
+ slot_name = /** @type {Text[] } */ ( slot . value ) [ 0 ] . data ;
122
+
123
+ lets [ slot_name ] = child . attributes . filter ( ( attribute ) => attribute . type === 'LetDirective' ) ;
124
+ } else if ( child . type === 'SvelteFragment' ) {
125
+ lets . default . push (
126
+ ...child . attributes . filter ( ( attribute ) => attribute . type === 'LetDirective' )
127
+ ) ;
125
128
}
126
129
}
127
130
@@ -152,16 +155,40 @@ export function serialize_inline_component(node, expression, context) {
152
155
153
156
if ( block . body . length === 0 ) continue ;
154
157
155
- const slot_fn = b . arrow (
156
- [ b . id ( '$$payload' ) , b . id ( '$$slotProps' ) ] ,
157
- b . block ( [
158
- ...( slot_name === 'default' && ! slot_scope_applies_to_itself ? lets : [ ] ) ,
159
- ...block . body
160
- ] )
161
- ) ;
158
+ /** @type {Pattern[] } */
159
+ const params = [ b . id ( '$$payload' ) ] ;
160
+
161
+ if ( lets [ slot_name ] . length > 0 ) {
162
+ const pattern = b . object_pattern (
163
+ lets [ slot_name ] . map ( ( node ) => {
164
+ if ( node . expression === null ) {
165
+ return b . init ( node . name , b . id ( node . name ) ) ;
166
+ }
167
+
168
+ if ( node . expression . type === 'ObjectExpression' ) {
169
+ // @ts -expect-error TODO it should be an `ObjectPattern`, not an `ObjectExpression`
170
+ return b . init ( node . name , b . object_pattern ( node . expression . properties ) ) ;
171
+ }
172
+
173
+ if ( node . expression . type === 'ArrayExpression' ) {
174
+ // @ts -expect-error TODO ditto
175
+ return b . init ( node . name , b . array_pattern ( node . expression . elements ) ) ;
176
+ }
177
+
178
+ return b . init ( node . name , node . expression ) ;
179
+ } )
180
+ ) ;
181
+
182
+ params . push ( pattern ) ;
183
+ }
184
+
185
+ const slot_fn = b . arrow ( params , b . block ( block . body ) ) ;
162
186
163
187
if ( slot_name === 'default' && ! has_children_prop ) {
164
- if ( lets . length === 0 && children . default . every ( ( node ) => node . type !== 'SvelteFragment' ) ) {
188
+ if (
189
+ lets . default . length === 0 &&
190
+ children . default . every ( ( node ) => node . type !== 'SvelteFragment' )
191
+ ) {
165
192
// create `children` prop...
166
193
push_prop ( b . prop ( 'init' , b . id ( 'children' ) , slot_fn ) ) ;
167
194
0 commit comments