bind:value causes stack overflow on 5.45.3 (in dev) when used with Bun
Describe the bug
I'm in the process of rewriting an app from pure vanilla JS to Svelte. I'm mostly done now, and today I've been moving a bunch of CSS from a single .css file to components.
At some point I've noticed that at least two unrelated components are causing a stack overflow:
HomeSearch.svelte:92 Uncaught RangeError: Maximum call stack size exceeded
in <unknown>
in App.svelte
at get (HomeSearch.svelte:92:15)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
at get (HomeSearch.svelte:93:13)
Stack trace links point to a place in generated code that looks like this:
$.bind_value(
input,
function get() {
return $.get(query);
},
function set($$value) {
$.set(query, $$value);
}
);
At first I thought I broke something with the CSS changes, but I realized that I also installed the latest patch update at some point today.
This seems to be happening even in a component as simple as this:
<script>
let query = $state('');
</script>
<input type="text" bind:value={query}>
After some debugging I found that this only happens if the bundler (Bun) runs in dev mode, not in production mode. The difference between the two versions of the bundle include a part like this, which seems to be the issue since changing this removes the issue:
< bind_value(input, () => get(query), ($$value) => set(query, $$value));
---
> bind_value(input, function get() {
> return get(query);
> }, function set($$value) {
> set(query, $$value);
> });
In version 5.45.2, both dev and prod builds create the top version. It seems to me that… this function get() is calling itself here? Which doesn't happen in the production version and in the previous release, where it was always printed as the arrow function.
This also seems to only happen if minification is turned off in the bundler (which I'm guessing renames such functions to some one-letter name).
My guess is that this is caused by this PR: https://github.com/sveltejs/svelte/pull/17269, and I think it might be somewhere in https://github.com/sveltejs/svelte/pull/17269/files#diff-59f747db7cbab9adbb6aa21ae06ab8558320d1e6b4fa43f209a0d5fe67ce01fe around this code:
if (dev) {
// in dev, create named functions, so that `$inspect(...)` delivers
// useful stack traces
get = b.function(b.id('get', node.name_loc), [], b.block([b.return(expression)]));
set = b.function(
b.id('set', node.name_loc),
Reproduction
- update to 5.45.3
- build such component:
<script>
let query = $state('');
</script>
<input type="text" bind:value={query}>
- disable minification
- make a development build
- run app
Logs
System Info
n/a, running Bun
Severity
blocking an upgrade
Have you tried creating an issue in the bun repo? Seems this could be an issue on their end?
Maybe caused by #17266, cc @adiguba
@dummdidumm I think this is caused by this PR: https://github.com/sveltejs/svelte/pull/17269, there are some changes there that create named functions in dev mode:
if (dev) {
// in dev, create named functions, so that `$inspect(...)` delivers
// useful stack traces
get = b.function(b.id('get', node.name_loc), [], b.block([b.return(expression)]));
set = b.function(
b.id('set', node.name_loc),
[b.id('$$value')],
b.block([b.stmt(assignment)])
);
} else {
// in prod, optimise for brevity
get = b.thunk(expression);
...
}
Btw, I've tried in ESBuild now, and there the output in dev mode is generated with functions renamed differently that don't end up calling themselves:
bind_value(
input,
/* @__PURE__ */ __name(function get3() {
return get(query);
}, "get"),
/* @__PURE__ */ __name(function set2($$value) {
set(query, $$value);
}, "set")
);
In the Bun output, that function get3 is named get.
I don't thinks it's the PR 17266. It very simple and only add a warning on props. It do no change the generated code.