1- /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, Statement } from 'estree' */
1+ /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, Statement, SpreadElement } from 'estree' */
22/** @import { AST } from '#compiler' */
33/** @import { ComponentContext } from '../../types.js' */
44import { dev , is_ignored } from '../../../../../state.js' ;
@@ -10,6 +10,39 @@ import { build_attribute_value } from '../shared/element.js';
1010import { build_event_handler } from './events.js' ;
1111import { determine_slot } from '../../../../../utils/slot.js' ;
1212
13+ /**
14+ * @param {Property } prop
15+ * @param {ComponentContext } context
16+ * @returns {boolean }
17+ */
18+ function is_attachments_prop ( prop , context ) {
19+ if ( prop . key . type !== 'Identifier' ) {
20+ return false ;
21+ }
22+
23+ const binding = context . state . scope ?. get ?. ( prop . key . name ) ?? undefined ;
24+ const expression = prop . computed && prop . key . type === 'Identifier' ? prop . key : binding ?. initial ;
25+
26+ if ( ! expression || expression . type !== 'CallExpression' ) {
27+ return false ;
28+ }
29+
30+ if (
31+ expression . callee . type !== 'MemberExpression' ||
32+ expression . callee . object . type !== 'Identifier' ||
33+ expression . callee . object . name !== 'Symbol' ||
34+ expression . callee . property . type !== 'Identifier' ||
35+ expression . callee . property . name !== 'for' ||
36+ expression . arguments . length !== 1 ||
37+ expression . arguments [ 0 ] . type !== 'Literal' ||
38+ expression . arguments [ 0 ] . value !== 'svelte.attachments'
39+ ) {
40+ return false ;
41+ }
42+
43+ return true ;
44+ }
45+
1346/**
1447 * @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf } node
1548 * @param {string } component_name
@@ -50,6 +83,9 @@ export function build_component(node, component_name, context, anchor = context.
5083 /** @type {ExpressionStatement[] } */
5184 const binding_initializers = [ ] ;
5285
86+ /** @type {Array<Expression | SpreadElement> } */
87+ const all_attachments = [ ] ;
88+
5389 /**
5490 * If this component has a slot property, it is a named slot within another component. In this case
5591 * the slot scope applies to the component itself, too, and not just its children.
@@ -262,24 +298,36 @@ export function build_component(node, component_name, context, anchor = context.
262298 }
263299 }
264300 } else if ( attribute . type === 'Attachment' ) {
265- // TODO do we need to create a derived here?
266301 for ( const attachment of attribute . attachments ) {
267- push_prop (
268- b . prop (
269- 'get' ,
270- b . call ( "Symbol.for('svelte.attachments')" ) ,
271- /** @type {Expression } */ (
272- context . visit ( attachment . type === 'SpreadElement' ? attachment . argument : attachment )
273- ) ,
274- true
275- )
276- ) ;
302+ if ( attachment . type === 'SpreadElement' ) {
303+ const visited = /** @type {ExpressionStatement } */ ( context . visit ( attachment . argument ) ) ;
304+ all_attachments . push ( b . spread ( visited . expression ) ) ;
305+ } else {
306+ const visited = /** @type {ExpressionStatement } */ ( context . visit ( attachment ) ) ;
307+ all_attachments . push ( visited . expression ) ;
308+ }
277309 }
278310 }
279311 }
280312
281313 delayed_props . forEach ( ( fn ) => fn ( ) ) ;
282314
315+ if ( all_attachments . length > 0 ) {
316+ const attachment_symbol = b . member (
317+ b . id ( 'Symbol' ) ,
318+ b . call ( 'for' , b . literal ( 'svelte.attachments' ) )
319+ ) ;
320+
321+ push_prop (
322+ b . prop (
323+ 'init' ,
324+ attachment_symbol ,
325+ b . array ( all_attachments ) ,
326+ true // Mark as computed property
327+ )
328+ ) ;
329+ }
330+
283331 if ( slot_scope_applies_to_itself ) {
284332 context . state . init . push ( ...lets ) ;
285333 }
0 commit comments