Skip to content

Commit 3ad1e0a

Browse files
committed
wip support adding attachments to attachments prop array
1 parent 3015528 commit 3ad1e0a

File tree

2 files changed

+88
-22
lines changed

2 files changed

+88
-22
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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' */
44
import { dev, is_ignored } from '../../../../../state.js';
@@ -10,6 +10,39 @@ import { build_attribute_value } from '../shared/element.js';
1010
import { build_event_handler } from './events.js';
1111
import { 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
}

packages/svelte/src/compiler/utils/builders.js

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,21 @@ export function function_declaration(id, params, body) {
229229
}
230230

231231
/**
232-
* @param {string} name
232+
* @param {string | ESTree.Expression} name_or_expr
233233
* @param {ESTree.Statement[]} body
234-
* @returns {ESTree.Property & { value: ESTree.FunctionExpression}}}
235-
*/
236-
export function get(name, body) {
237-
return prop('get', key(name), function_builder(null, [], block(body)));
234+
* @returns {ESTree.Property & { value: ESTree.FunctionExpression }}
235+
*/
236+
export function get(name_or_expr, body) {
237+
let key_expr;
238+
let computed = false;
239+
if (typeof name_or_expr === 'string') {
240+
key_expr = key(name_or_expr);
241+
computed = key_expr.type !== 'Identifier';
242+
} else {
243+
key_expr = name_or_expr;
244+
computed = true;
245+
}
246+
return prop('get', key_expr, function_builder(null, [], block(body)), computed);
238247
}
239248

240249
/**
@@ -380,12 +389,21 @@ export function sequence(expressions) {
380389
}
381390

382391
/**
383-
* @param {string} name
392+
* @param {string | ESTree.Expression} name_or_expr
384393
* @param {ESTree.Statement[]} body
385-
* @returns {ESTree.Property & { value: ESTree.FunctionExpression}}
386-
*/
387-
export function set(name, body) {
388-
return prop('set', key(name), function_builder(null, [id('$$value')], block(body)));
394+
* @returns {ESTree.Property & { value: ESTree.FunctionExpression }}
395+
*/
396+
export function set(name_or_expr, body) {
397+
let key_expr;
398+
let computed = false;
399+
if (typeof name_or_expr === 'string') {
400+
key_expr = key(name_or_expr);
401+
computed = key_expr.type !== 'Identifier';
402+
} else {
403+
key_expr = name_or_expr;
404+
computed = true;
405+
}
406+
return prop('set', key_expr, function_builder(null, [id('$$value')], block(body)), computed);
389407
}
390408

391409
/**

0 commit comments

Comments
 (0)