Skip to content

Commit 6351288

Browse files
fix: falsy attachments on components (#16021)
* fix: falsy attachments on components * skip the noop if known to be a function --------- Co-authored-by: Rich Harris <[email protected]>
1 parent d4af83a commit 6351288

File tree

6 files changed

+57
-2
lines changed

6 files changed

+57
-2
lines changed

.changeset/fuzzy-taxis-hunt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: falsy attachments on components

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,18 @@ export function build_component(node, component_name, context, anchor = context.
258258
}
259259
}
260260
} else if (attribute.type === 'AttachTag') {
261+
const evaluated = context.state.scope.evaluate(attribute.expression);
262+
261263
let expression = /** @type {Expression} */ (context.visit(attribute.expression));
262264

263265
if (attribute.metadata.expression.has_state) {
264-
expression = b.arrow([b.id('$$node')], b.call(expression, b.id('$$node')));
266+
expression = b.arrow(
267+
[b.id('$$node')],
268+
b.call(
269+
evaluated.is_function ? expression : b.logical('||', expression, b.id('$.noop')),
270+
b.id('$$node')
271+
)
272+
);
265273
}
266274

267275
push_prop(b.prop('get', b.call('$.attachment'), expression, true));

packages/svelte/src/compiler/phases/scope.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const UNKNOWN = Symbol('unknown');
2020
/** Includes `BigInt` */
2121
export const NUMBER = Symbol('number');
2222
export const STRING = Symbol('string');
23+
export const FUNCTION = Symbol('string');
2324

2425
/** @type {Record<string, [type: NUMBER | STRING | UNKNOWN, fn?: Function]>} */
2526
const globals = {
@@ -200,6 +201,13 @@ class Evaluation {
200201
*/
201202
is_number = true;
202203

204+
/**
205+
* True if the value is known to be a function
206+
* @readonly
207+
* @type {boolean}
208+
*/
209+
is_function = true;
210+
203211
/**
204212
* @readonly
205213
* @type {any}
@@ -209,7 +217,7 @@ class Evaluation {
209217
/**
210218
*
211219
* @param {Scope} scope
212-
* @param {Expression} expression
220+
* @param {Expression | FunctionDeclaration} expression
213221
* @param {Set<any>} values
214222
*/
215223
constructor(scope, expression, values) {
@@ -500,6 +508,13 @@ class Evaluation {
500508
break;
501509
}
502510

511+
case 'ArrowFunctionExpression':
512+
case 'FunctionExpression':
513+
case 'FunctionDeclaration': {
514+
this.values.add(FUNCTION);
515+
break;
516+
}
517+
503518
default: {
504519
this.values.add(UNKNOWN);
505520
}
@@ -516,6 +531,10 @@ class Evaluation {
516531
this.is_number = false;
517532
}
518533

534+
if (value !== FUNCTION) {
535+
this.is_function = false;
536+
}
537+
519538
if (value == null || value === UNKNOWN) {
520539
this.is_defined = false;
521540
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let props = $props();
3+
</script>
4+
5+
<div {...props}></div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
test() {}
5+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
4+
function attachment(){
5+
console.log("up");
6+
}
7+
8+
let enabled = $state(false);
9+
</script>
10+
11+
<button onclick={() => enabled = !enabled}></button>
12+
13+
<Child {@attach enabled && attachment} />

0 commit comments

Comments
 (0)