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 <rich.harris@vercel.com>
pull/16023/head
Paolo Ricciuti 4 months ago committed by GitHub
parent d4af83a058
commit 6351288c7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: falsy attachments on components

@ -258,10 +258,18 @@ export function build_component(node, component_name, context, anchor = context.
} }
} }
} else if (attribute.type === 'AttachTag') { } else if (attribute.type === 'AttachTag') {
const evaluated = context.state.scope.evaluate(attribute.expression);
let expression = /** @type {Expression} */ (context.visit(attribute.expression)); let expression = /** @type {Expression} */ (context.visit(attribute.expression));
if (attribute.metadata.expression.has_state) { if (attribute.metadata.expression.has_state) {
expression = b.arrow([b.id('$$node')], b.call(expression, b.id('$$node'))); expression = b.arrow(
[b.id('$$node')],
b.call(
evaluated.is_function ? expression : b.logical('||', expression, b.id('$.noop')),
b.id('$$node')
)
);
} }
push_prop(b.prop('get', b.call('$.attachment'), expression, true)); push_prop(b.prop('get', b.call('$.attachment'), expression, true));

@ -20,6 +20,7 @@ const UNKNOWN = Symbol('unknown');
/** Includes `BigInt` */ /** Includes `BigInt` */
export const NUMBER = Symbol('number'); export const NUMBER = Symbol('number');
export const STRING = Symbol('string'); export const STRING = Symbol('string');
export const FUNCTION = Symbol('string');
/** @type {Record<string, [type: NUMBER | STRING | UNKNOWN, fn?: Function]>} */ /** @type {Record<string, [type: NUMBER | STRING | UNKNOWN, fn?: Function]>} */
const globals = { const globals = {
@ -200,6 +201,13 @@ class Evaluation {
*/ */
is_number = true; is_number = true;
/**
* True if the value is known to be a function
* @readonly
* @type {boolean}
*/
is_function = true;
/** /**
* @readonly * @readonly
* @type {any} * @type {any}
@ -209,7 +217,7 @@ class Evaluation {
/** /**
* *
* @param {Scope} scope * @param {Scope} scope
* @param {Expression} expression * @param {Expression | FunctionDeclaration} expression
* @param {Set<any>} values * @param {Set<any>} values
*/ */
constructor(scope, expression, values) { constructor(scope, expression, values) {
@ -500,6 +508,13 @@ class Evaluation {
break; break;
} }
case 'ArrowFunctionExpression':
case 'FunctionExpression':
case 'FunctionDeclaration': {
this.values.add(FUNCTION);
break;
}
default: { default: {
this.values.add(UNKNOWN); this.values.add(UNKNOWN);
} }
@ -516,6 +531,10 @@ class Evaluation {
this.is_number = false; this.is_number = false;
} }
if (value !== FUNCTION) {
this.is_function = false;
}
if (value == null || value === UNKNOWN) { if (value == null || value === UNKNOWN) {
this.is_defined = false; this.is_defined = false;
} }

@ -0,0 +1,5 @@
<script>
let props = $props();
</script>
<div {...props}></div>

@ -0,0 +1,5 @@
import { test } from '../../test';
export default test({
test() {}
});

@ -0,0 +1,13 @@
<script>
import Child from './Child.svelte';
function attachment(){
console.log("up");
}
let enabled = $state(false);
</script>
<button onclick={() => enabled = !enabled}></button>
<Child {@attach enabled && attachment} />
Loading…
Cancel
Save