Skip to content

Commit 7486eab

Browse files
authored
fix: make effects depend on state created inside them (#16198)
* make effects depend on state created inside them * fix, add github action * disable test in async mode
1 parent d137eca commit 7486eab

File tree

13 files changed

+108
-20
lines changed

13 files changed

+108
-20
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ jobs:
4343
- run: pnpm test
4444
env:
4545
CI: true
46+
TestNoAsync:
47+
permissions: {}
48+
runs-on: ubuntu-latest
49+
timeout-minutes: 10
50+
steps:
51+
- uses: actions/checkout@v4
52+
- uses: pnpm/action-setup@v4
53+
- uses: actions/setup-node@v4
54+
with:
55+
node-version: 22
56+
cache: pnpm
57+
- run: pnpm install --frozen-lockfile
58+
- run: pnpm playwright install chromium
59+
- run: pnpm test runtime-runes
60+
env:
61+
CI: true
62+
SVELTE_NO_ASYNC: true
4663
Lint:
4764
permissions: {}
4865
runs-on: ubuntu-latest

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ import {
5757
import * as w from './warnings.js';
5858
import { current_batch, Batch, batch_deriveds } from './reactivity/batch.js';
5959
import { handle_error, invoke_error_boundary } from './error-handling.js';
60-
import { snapshot } from '../shared/clone.js';
6160

6261
/** @type {Effect | null} */
6362
let last_scheduled_effect = null;
@@ -259,7 +258,12 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true)
259258
for (var i = 0; i < reactions.length; i++) {
260259
var reaction = reactions[i];
261260

262-
if (reaction_sources?.[1].includes(signal) && reaction_sources[0] === active_reaction) continue;
261+
if (
262+
!async_mode_flag &&
263+
reaction_sources?.[1].includes(signal) &&
264+
reaction_sources[0] === active_reaction
265+
)
266+
continue;
263267

264268
if ((reaction.f & DERIVED) !== 0) {
265269
schedule_possible_effect_self_invalidation(/** @type {Derived} */ (reaction), effect, false);
@@ -299,7 +303,9 @@ export function update_reaction(reaction) {
299303
untracking = false;
300304
read_version++;
301305

302-
reaction.f |= EFFECT_IS_UPDATING;
306+
if (!async_mode_flag || (reaction.f & DERIVED) !== 0) {
307+
reaction.f |= EFFECT_IS_UPDATING;
308+
}
303309

304310
if (reaction.ac !== null) {
305311
reaction.ac?.abort(STALE_REACTION);
@@ -383,7 +389,9 @@ export function update_reaction(reaction) {
383389
set_component_context(previous_component_context);
384390
untracking = previous_untracking;
385391

386-
reaction.f ^= EFFECT_IS_UPDATING;
392+
if (!async_mode_flag || (reaction.f & DERIVED) !== 0) {
393+
reaction.f ^= EFFECT_IS_UPDATING;
394+
}
387395
}
388396
}
389397

@@ -776,7 +784,9 @@ export function get(signal) {
776784

777785
if (
778786
!destroyed &&
779-
(!reaction_sources?.[1].includes(signal) || reaction_sources[0] !== active_reaction)
787+
((async_mode_flag && (active_reaction.f & DERIVED) === 0) ||
788+
!reaction_sources?.[1].includes(signal) ||
789+
reaction_sources[0] !== active_reaction)
780790
) {
781791
var deps = active_reaction.deps;
782792

packages/svelte/src/internal/flags/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export function enable_async_mode_flag() {
66
async_mode_flag = true;
77
}
88

9+
/** ONLY USE THIS DURING TESTING */
10+
export function disable_async_mode_flag() {
11+
async_mode_flag = false;
12+
}
13+
914
export function enable_legacy_mode_flag() {
1015
legacy_mode_flag = true;
1116
}

packages/svelte/tests/helpers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ if (typeof window !== 'undefined') {
194194

195195
export const fragments = /** @type {'html' | 'tree'} */ (process.env.FRAGMENTS) ?? 'html';
196196

197+
export const async_mode = process.env.SVELTE_NO_ASYNC !== 'true';
198+
197199
/**
198200
* @param {any[]} logs
199201
*/

packages/svelte/tests/runtime-legacy/shared.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { setImmediate } from 'node:timers/promises';
33
import { globSync } from 'tinyglobby';
44
import { createClassComponent } from 'svelte/legacy';
55
import { proxy } from 'svelte/internal/client';
6-
import { flushSync, hydrate, mount, unmount, untrack } from 'svelte';
6+
import { flushSync, hydrate, mount, unmount } from 'svelte';
77
import { render } from 'svelte/server';
88
import { afterAll, assert, beforeAll } from 'vitest';
9-
import { compile_directory, fragments } from '../helpers.js';
9+
import { async_mode, compile_directory, fragments } from '../helpers.js';
1010
import { assert_html_equal, assert_html_equal_with_options } from '../html_equal.js';
1111
import { raf } from '../animation-helpers.js';
1212
import type { CompileOptions } from '#compiler';
@@ -45,6 +45,10 @@ export interface RuntimeTest<Props extends Record<string, any> = Record<string,
4545
mode?: Array<'server' | 'client' | 'hydrate'>;
4646
/** Temporarily skip specific modes, without skipping the entire test */
4747
skip_mode?: Array<'server' | 'client' | 'hydrate'>;
48+
/** Skip if running with process.env.NO_ASYNC */
49+
skip_no_async?: boolean;
50+
/** Skip if running without process.env.NO_ASYNC */
51+
skip_async?: boolean;
4852
html?: string;
4953
ssrHtml?: string;
5054
compileOptions?: Partial<CompileOptions>;
@@ -121,7 +125,15 @@ let console_error = console.error;
121125
export function runtime_suite(runes: boolean) {
122126
return suite_with_variants<RuntimeTest, 'hydrate' | 'ssr' | 'dom', CompileOptions>(
123127
['dom', 'hydrate', 'ssr'],
124-
(variant, config) => {
128+
(variant, config, test_name) => {
129+
if (!async_mode && (config.skip_no_async || test_name.startsWith('async-'))) {
130+
return true;
131+
}
132+
133+
if (async_mode && config.skip_async) {
134+
return true;
135+
}
136+
125137
if (variant === 'hydrate') {
126138
if (config.mode && !config.mode.includes('hydrate')) return 'no-test';
127139
if (config.skip_mode?.includes('hydrate')) return true;
@@ -169,7 +181,7 @@ async function common_setup(cwd: string, runes: boolean | undefined, config: Run
169181
dev: force_hmr ? true : undefined,
170182
hmr: force_hmr ? true : undefined,
171183
experimental: {
172-
async: runes
184+
async: runes && async_mode
173185
},
174186
fragments,
175187
...config.compileOptions,

packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { async_mode } from '../../../helpers';
12
import { test } from '../../test';
23
import { flushSync } from 'svelte';
34

@@ -10,6 +11,12 @@ export default test({
1011
flushSync(() => {
1112
b1.click();
1213
});
13-
assert.deepEqual(logs, ['init 0']);
14+
15+
// With async mode (which is on by default for runtime-runes) this works as expected, without it
16+
// it works differently: https://github.com/sveltejs/svelte/pull/15564
17+
assert.deepEqual(
18+
logs,
19+
async_mode ? ['init 0', 'cleanup 2', null, 'init 2', 'cleanup 4', null, 'init 4'] : ['init 0']
20+
);
1421
}
1522
});

packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
})
1515
</script>
1616

17-
<button on:click={() => count++ }>Click</button>
17+
<button onclick={() => count++ }>Click</button>

packages/svelte/tests/runtime-runes/samples/set-context-after-await/_config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { test } from '../../test';
22

33
export default test({
4+
skip_no_async: true,
45
async test({ assert, logs }) {
56
await Promise.resolve();
67
await Promise.resolve();
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { flushSync } from 'svelte';
22
import { test } from '../../test';
3+
import { async_mode } from '../../../helpers';
34

45
export default test({
56
async test({ target, assert, logs }) {
67
const button = target.querySelector('button');
78

89
flushSync(() => button?.click());
9-
assert.ok(logs[0].startsWith('set_context_after_init'));
10+
assert.ok(
11+
async_mode
12+
? logs[0].startsWith('set_context_after_init')
13+
: logs[0] === 'works without experimental async but really shouldnt'
14+
);
1015
}
1116
});

packages/svelte/tests/runtime-runes/samples/set-context-after-mount/main.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
if (condition) {
88
try {
99
setContext('potato', {});
10+
console.log('works without experimental async but really shouldnt')
1011
} catch (e) {
1112
console.log(e.message);
1213
}

0 commit comments

Comments
 (0)