diff --git a/.changeset/silver-ties-itch.md b/.changeset/silver-ties-itch.md
new file mode 100644
index 0000000000..67e33dadfb
--- /dev/null
+++ b/.changeset/silver-ties-itch.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
index 98036aa9b6..018bdacc5e 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
@@ -29,7 +29,8 @@ import {
build_render_statement,
build_template_chunk,
build_update_assignment,
- get_expression_id
+ get_expression_id,
+ memoize_expression
} from './shared/utils.js';
import { visit_event_attribute } from './shared/events.js';
@@ -532,18 +533,30 @@ function build_element_attribute_update_assignment(
const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg';
const is_mathml = context.state.metadata.namespace === 'mathml';
+ const is_autofocus = name === 'autofocus';
+
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
- metadata.has_call ? get_expression_id(state, value) : value
+ metadata.has_call
+ ? // if it's autofocus we will not add this to a template effect so we don't want to get the expression id
+ // but separately memoize the expression
+ is_autofocus
+ ? memoize_expression(state, value)
+ : get_expression_id(state, value)
+ : value
);
- if (name === 'autofocus') {
+ if (is_autofocus) {
state.init.push(b.stmt(b.call('$.autofocus', node_id, value)));
return false;
}
// Special case for Firefox who needs it set as a property in order to work
if (name === 'muted') {
- state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value)));
+ if (!has_state) {
+ state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value)));
+ return false;
+ }
+ state.update.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value)));
return false;
}
@@ -660,8 +673,18 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co
*/
function build_element_special_value_attribute(element, node_id, attribute, context) {
const state = context.state;
+ const is_select_with_value =
+ // attribute.metadata.dynamic would give false negatives because even if the value does not change,
+ // the inner options could still change, so we need to always treat it as reactive
+ element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
+
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
- metadata.has_call ? get_expression_id(state, value) : value
+ metadata.has_call
+ ? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately
+ is_select_with_value
+ ? memoize_expression(context.state, value)
+ : get_expression_id(state, value)
+ : value
);
const inner_assignment = b.assignment(
@@ -674,11 +697,6 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
)
);
- const is_select_with_value =
- // attribute.metadata.dynamic would give false negatives because even if the value does not change,
- // the inner options could still change, so we need to always treat it as reactive
- element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
-
const update = b.stmt(
is_select_with_value
? b.sequence([
diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js
new file mode 100644
index 0000000000..0597c2fda8
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, errors }) {
+ assert.deepEqual(errors, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte
new file mode 100644
index 0000000000..cb3804af34
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js
new file mode 100644
index 0000000000..cc4dfb37f0
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js
@@ -0,0 +1,13 @@
+import { flushSync } from 'svelte';
+import { ok, test } from '../../test';
+
+export default test({
+ async test({ assert, target, logs }) {
+ const btn = target.querySelector('button');
+ ok(btn);
+ flushSync(() => {
+ btn.click();
+ });
+ assert.deepEqual(logs, [true]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte
new file mode 100644
index 0000000000..646334c1ec
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js
new file mode 100644
index 0000000000..0597c2fda8
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, errors }) {
+ assert.deepEqual(errors, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte
new file mode 100644
index 0000000000..b1d60ecf6d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file