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/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 2c60fc8313..2f05dca73b 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -187,6 +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 + // TODO this is wrong when flushSync is called while another batch is active, can be two when there's actually no time traveling if (batches.size > 1) { current_values = new Map(); batch_deriveds = new Map(); @@ -484,7 +485,8 @@ export class Batch { */ export function flushSync(fn) { if (async_mode_flag && active_effect !== null) { - e.flush_sync_in_effect(); + // TODO why do we disallow this? + // e.flush_sync_in_effect(); } var was_flushing_sync = is_flushing_sync; @@ -622,7 +624,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/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index d4a053d1aa..79916bd3c1 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -11,7 +11,6 @@ import * as w from '../internal/client/warnings.js'; import { DEV } from 'esm-env'; import { FILENAME } from '../constants.js'; import { component_context, dev_current_component_function } from '../internal/client/context.js'; -import { async_mode_flag } from '../internal/flags/index.js'; /** * Takes the same options as a Svelte 4 component and the component function and returns a Svelte 4 compatible component. @@ -121,9 +120,8 @@ class Svelte4Component { recover: options.recover }); - // We don't flushSync for custom element wrappers or if the user doesn't want it, - // or if we're in async mode since `flushSync()` will fail - if (!async_mode_flag && (!options?.props?.$$host || options.sync === false)) { + // We don't flushSync for custom element wrappers or if the user doesn't want it + if (!options?.props?.$$host || options.sync === false) { flushSync(); } 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..b34a90e901 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/flush-sync-inside-attachment/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + assert.htmlEqual(target.innerHTML, `