fix: ensure fork always accesses correct values

Not all batches will flush right after being activated, some will be activated and then `get` is called on a signal. In that case the value was wrong because we did not apply the changes of that batch. By doing `this.apply()` during `activate()` we ensure we do, which fixes (among other things, likely) a forking bug where old values where sneaking in.

Fixes #17079
pull/17098/head
Simon Holthausen 5 days ago
parent b7625fd42c
commit 02f8d07364

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure fork always accesses correct values

@ -15,7 +15,8 @@ import {
DERIVED,
BOUNDARY_EFFECT,
EAGER_EFFECT,
HEAD_EFFECT
HEAD_EFFECT,
ERROR_VALUE
} from '#client/constants';
import { async_mode_flag } from '../../flags/index.js';
import { deferred, define_property } from '../../shared/utils.js';
@ -285,12 +286,16 @@ export class Batch {
this.previous.set(source, value);
}
this.current.set(source, source.v);
batch_values?.set(source, source.v);
// Don't save this as it would mean it's not thrown in the `runtime.js#get` function
if ((source.f & ERROR_VALUE) === 0) {
this.current.set(source, source.v);
batch_values?.set(source, source.v);
}
}
activate() {
current_batch = this;
this.apply();
}
deactivate() {
@ -492,7 +497,7 @@ export class Batch {
}
apply() {
if (!async_mode_flag || batches.size === 1) return;
if (!async_mode_flag || (!this.is_fork && batches.size === 1)) return;
// if there are multiple batches, we are 'time travelling' —
// we need to override values with the ones in this batch...

@ -0,0 +1,8 @@
<script>
let { x } = $props();
console.log(x);
await Promise.resolve();
console.log(x);
</script>
{x}

@ -0,0 +1,12 @@
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const btn = target.querySelector('button');
btn?.click();
await new Promise((r) => setTimeout(r, 2));
assert.htmlEqual(target.innerHTML, `<button>fork</button> universe`);
assert.deepEqual(logs, ['universe', 'universe']);
}
});

@ -0,0 +1,17 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
</script>
<button onclick={async () => {
const f = fork(() => {
x = 'universe'
});
await new Promise(r => setTimeout(r));
f.commit();
}}>fork</button>
{#if x === 'universe'}
<Child {x} />
{/if}
Loading…
Cancel
Save