Skip to content

Commit 22435b2

Browse files
committed
failing test (that uncovered other unrelated bug) + fix
1 parent 1032b31 commit 22435b2

File tree

4 files changed

+72
-5
lines changed

4 files changed

+72
-5
lines changed

packages/svelte/src/internal/client/runtime.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -664,15 +664,18 @@ function reconnect(derived) {
664664

665665
/**
666666
* Removes the WAS_MARKED flag from the derived and its dependencies
667-
* @param {Derived} derived
667+
* @param {Value} derived
668668
*/
669669
function remove_marked_flag(derived) {
670-
if ((derived.f & WAS_MARKED) === 0) return;
671-
derived.f ^= WAS_MARKED;
670+
// We cannot stop at the first non-marked derived because batch_values can
671+
// cause "holes" of unmarked deriveds in an otherwise marked graph
672+
if ((derived.f & DERIVED) === 0) return;
673+
674+
derived.f &= ~WAS_MARKED;
672675

673676
// Only deriveds with dependencies can be marked
674-
for (const dep of /** @type {Value[]} */ (derived.deps)) {
675-
remove_marked_flag(/** @type {Derived} */ (dep));
677+
for (const dep of /** @type {Value[]} */ (/** @type {Derived} */ (derived).deps)) {
678+
remove_marked_flag(dep);
676679
}
677680
}
678681

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script>
2+
import { untrack } from "svelte";
3+
4+
let { double } = $props();
5+
6+
// Test setup:
7+
// - component initialized while pending work
8+
// - derived that depends on mulitple sources
9+
// - indirect updates to subsequent deriveds
10+
// - two sibling effects where the former influences the latter
11+
// - first effect reads derived of second inside untrack
12+
let x = $state(0);
13+
const other = $derived(double + x);
14+
const another = $derived(other + 1);
15+
const another2 = $derived(another + 1);
16+
17+
$effect(() => {
18+
untrack(() => {
19+
another2;
20+
x++
21+
});
22+
});
23+
24+
$effect(() => {
25+
console.log(another2);
26+
})
27+
</script>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target, logs }) {
6+
const button = target.querySelector('button');
7+
8+
button?.click();
9+
await tick();
10+
// TODO this is wrong: it should be [5]
11+
assert.deepEqual(logs, [4]);
12+
13+
button?.click();
14+
await tick();
15+
// TODO this is wrong: it should be [5, 7]
16+
assert.deepEqual(logs, [4, 7]);
17+
}
18+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
import Component from './Component.svelte';
3+
let count = $state(0);
4+
const double = $derived(count * 2);
5+
</script>
6+
7+
<svelte:boundary>
8+
{await new Promise((r) => {
9+
// long enough for the test to do all its other stuff while this is pending
10+
setTimeout(r, 10);
11+
})}
12+
{#snippet pending()}{/snippet}
13+
</svelte:boundary>
14+
15+
<button onclick={() => count += 1}>{count}</button>
16+
17+
{#if count > 0}
18+
<Component {double} />
19+
{/if}

0 commit comments

Comments
 (0)