From bde51ed7dc3554eee50edc0c29e70a05e9ee842b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 26 Aug 2025 21:59:46 +0200 Subject: [PATCH] fix: don't fail on `flushSync` while flushing effects (#16674) * fix: don't fail on `flushSync` while flushing effects WIP fixes #16640 * cleanup, unrelated lint * revert * fix --- .changeset/honest-coins-work.md | 5 +++++ packages/svelte/elements.d.ts | 2 +- .../svelte/src/internal/client/reactivity/batch.js | 7 +++++-- .../flush-sync-inside-attachment/Child.svelte | 7 +++++++ .../samples/flush-sync-inside-attachment/_config.js | 12 ++++++++++++ .../flush-sync-inside-attachment/main.svelte | 13 +++++++++++++ 6 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 .changeset/honest-coins-work.md create mode 100644 packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/main.svelte diff --git a/.changeset/honest-coins-work.md b/.changeset/honest-coins-work.md new file mode 100644 index 0000000000..e6b68a8a67 --- /dev/null +++ b/.changeset/honest-coins-work.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't fail on `flushSync` while flushing effects diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index e604505d96..b0c2fae2de 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -1268,7 +1268,7 @@ export interface HTMLMetaAttributes extends HTMLAttributes { charset?: string | undefined | null; content?: string | undefined | null; 'http-equiv'?: - | 'accept-ch' + | 'accept-ch' | 'content-security-policy' | 'content-type' | 'default-style' diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 2c60fc8313..82f1de67a9 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -187,7 +187,7 @@ export class Batch { // if there are multiple batches, we are 'time travelling' — // we need to undo the changes belonging to any batch // other than the current one - if (batches.size > 1) { + if (async_mode_flag && batches.size > 1) { current_values = new Map(); batch_deriveds = new Map(); @@ -484,6 +484,7 @@ export class Batch { */ export function flushSync(fn) { if (async_mode_flag && active_effect !== null) { + // We disallow this because it creates super-hard to reason about stack trace and because it's generally a bad idea e.flush_sync_in_effect(); } @@ -622,7 +623,9 @@ function flush_queued_effects(effects) { } } - if (eager_block_effects.length > 0) { + // If update_effect() has a flushSync() in it, we may have flushed another flush_queued_effects(), + // which already handled this logic and did set eager_block_effects to null. + if (eager_block_effects?.length > 0) { // TODO this feels incorrect! it gets the tests passing old_values.clear(); diff --git a/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/Child.svelte b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/Child.svelte new file mode 100644 index 0000000000..44447e4f36 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/Child.svelte @@ -0,0 +1,7 @@ + + +{text} diff --git a/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/_config.js b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/_config.js new file mode 100644 index 0000000000..ec8858b2c6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/_config.js @@ -0,0 +1,12 @@ +import { async_mode } from '../../../helpers'; +import { test } from '../../test'; + +export default test({ + // In legacy mode this succeeds and logs 'hello' + // In async mode this throws an error because flushSync is called inside an effect + async test({ assert, target, logs }) { + assert.htmlEqual(target.innerHTML, `
hello
`); + assert.deepEqual(logs, ['hello']); + }, + runtime_error: async_mode ? 'flush_sync_in_effect' : undefined +}); diff --git a/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/main.svelte b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/main.svelte new file mode 100644 index 0000000000..bef820376b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/main.svelte @@ -0,0 +1,13 @@ + + + + +
{ + mount(Child, { target, props: { text: 'hello' } }); + flushSync(); +}}>