diff --git a/.changeset/slow-gorillas-yawn.md b/.changeset/slow-gorillas-yawn.md deleted file mode 100644 index 376b4d041c..0000000000 --- a/.changeset/slow-gorillas-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -breaking: avoid flushing queued updates on mount/hydrate diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 7f6b239114..4bb01a4899 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -81,7 +81,8 @@ export function set_text(text, value) { */ export function mount(component, options) { const anchor = options.anchor ?? options.target.appendChild(empty()); - return _mount(component, { ...options, anchor }); + // Don't flush previous effects to ensure order of outer effects stays consistent + return flush_sync(() => _mount(component, { ...options, anchor }), false); } /** @@ -114,35 +115,38 @@ export function hydrate(component, options) { const previous_hydrate_node = hydrate_node; try { - var anchor = /** @type {TemplateNode} */ (target.firstChild); - while ( - anchor && - (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) - ) { - anchor = /** @type {TemplateNode} */ (anchor.nextSibling); - } + // Don't flush previous effects to ensure order of outer effects stays consistent + return flush_sync(() => { + var anchor = /** @type {TemplateNode} */ (target.firstChild); + while ( + anchor && + (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) + ) { + anchor = /** @type {TemplateNode} */ (anchor.nextSibling); + } - if (!anchor) { - throw HYDRATION_ERROR; - } + if (!anchor) { + throw HYDRATION_ERROR; + } - set_hydrating(true); - set_hydrate_node(/** @type {Comment} */ (anchor)); - hydrate_next(); + set_hydrating(true); + set_hydrate_node(/** @type {Comment} */ (anchor)); + hydrate_next(); - const instance = _mount(component, { ...options, anchor }); + const instance = _mount(component, { ...options, anchor }); - if ( - hydrate_node.nodeType !== 8 || - /** @type {Comment} */ (hydrate_node).data !== HYDRATION_END - ) { - w.hydration_mismatch(); - throw HYDRATION_ERROR; - } + if ( + hydrate_node.nodeType !== 8 || + /** @type {Comment} */ (hydrate_node).data !== HYDRATION_END + ) { + w.hydration_mismatch(); + throw HYDRATION_ERROR; + } - set_hydrating(false); + set_hydrating(false); - return /** @type {Exports} */ (instance); + return instance; + }, false); } catch (error) { if (error === HYDRATION_ERROR) { // TODO it's possible for event listeners to have been added and diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 779548a65c..16facffabd 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -671,9 +671,10 @@ function process_effects(effect, collected_effects) { * Internal version of `flushSync` with the option to not flush previous effects. * Returns the result of the passed function, if given. * @param {() => any} [fn] + * @param {boolean} [flush_previous] * @returns {any} */ -export function flush_sync(fn) { +export function flush_sync(fn, flush_previous = true) { var previous_scheduler_mode = current_scheduler_mode; var previous_queued_root_effects = current_queued_root_effects; @@ -687,7 +688,9 @@ export function flush_sync(fn) { current_queued_root_effects = root_effects; is_micro_task_queued = false; - flush_queued_root_effects(previous_queued_root_effects); + if (flush_previous) { + flush_queued_root_effects(previous_queued_root_effects); + } var result = fn?.(); diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts index d592a65de3..fab0e5b308 100644 --- a/packages/svelte/tests/hydration/test.ts +++ b/packages/svelte/tests/hydration/test.ts @@ -8,7 +8,6 @@ import { suite, assert_ok, type BaseTest } from '../suite.js'; import { createClassComponent } from 'svelte/legacy'; import { render } from 'svelte/server'; import type { CompileOptions } from '#compiler'; -import { flushSync } from 'svelte'; interface HydrationTest extends BaseTest { load_compiled?: boolean; @@ -115,7 +114,6 @@ const { test, run } = suite(async (config, cwd) => { if (!override) { const expected = read(`${cwd}/_expected.html`) ?? rendered.html; - flushSync(); assert.equal(target.innerHTML.trim(), expected.trim()); } diff --git a/packages/svelte/tests/runtime-browser/driver.js b/packages/svelte/tests/runtime-browser/driver.js index ef6acd08f6..7a5603e9b8 100644 --- a/packages/svelte/tests/runtime-browser/driver.js +++ b/packages/svelte/tests/runtime-browser/driver.js @@ -5,7 +5,6 @@ import config from '__CONFIG__'; // @ts-expect-error import * as assert from 'assert.js'; import { createClassComponent } from 'svelte/legacy'; -import { flushSync } from 'svelte'; /** @param {HTMLElement} target */ export default async function (target) { @@ -46,8 +45,6 @@ export default async function (target) { } while (new Date().getTime() <= start + ms); }; - flushSync(); - if (config.html) { assert.htmlEqual(target.innerHTML, config.html); } diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js index 7596fd97be..7f3bfac707 100644 --- a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js @@ -1,4 +1,3 @@ -import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +9,6 @@ export default test({ inputs[1].dispatchEvent(new window.Event('change')); // Hydration shouldn't reset the value to 1 hydrate(); - flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js index 10dac59fae..e5bbe5b0fe 100644 --- a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js @@ -1,4 +1,3 @@ -import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +9,6 @@ export default test({ input.dispatchEvent(new window.Event('input')); // Hydration shouldn't reset the value to empty hydrate(); - flushSync(); assert.htmlEqual(target.innerHTML, '\nfoo'); }