diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index f8e3e5839f..2698ec5408 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -18,7 +18,8 @@ import { set_active_effect, set_active_reaction, reset_is_throwing_error, - schedule_effect + schedule_effect, + increment_write_version } from '../../runtime.js'; import { hydrate_next, @@ -170,11 +171,8 @@ export function boundary(node, props, children) { for (var [signal, entry] of forks) { if (signal.v !== entry.v) { if ((signal.f & DERIVED) === 0) { - var val = signal.v; - signal.v = entry.v; - internal_set(signal, val); - } else { - mark_reactions(signal, DIRTY); + mark_reactions(signal, DIRTY, undefined, true); + signal.wv = increment_write_version(); } } } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 9aff5c3250..289b5e3806 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -28,12 +28,14 @@ import { UNOWNED, MAYBE_DIRTY, BLOCK_EFFECT, - ROOT_EFFECT + ROOT_EFFECT, + ASYNC_DERIVED } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; +import { get_boundary } from '../dom/blocks/boundary.js'; export let inspect_effects = new Set(); @@ -169,6 +171,7 @@ export function set(source, value) { */ export function internal_set(source, value) { if (!source.equals(value)) { + possibly_fork(source); mark_reactions(source, DIRTY); @@ -258,9 +261,10 @@ export function update_pre(source, d = 1) { /** * @param {Value} signal * @param {number} status should be DIRTY or MAYBE_DIRTY + * @param {Value} [parent] * @returns {void} */ -export function mark_reactions(signal, status) { +export function mark_reactions(signal, status, parent, only_boundary = false) { var reactions = signal.reactions; if (reactions === null) return; @@ -277,6 +281,24 @@ export function mark_reactions(signal, status) { // In legacy mode, skip the current effect to prevent infinite loops if (!runes && reaction === active_effect) continue; + if (only_boundary) { + if ((flags & (DERIVED)) === 0) { + var boundary = get_boundary(/** @type {Effect} */ (reaction)); + if (!boundary) { + continue; + } + } + } else if ((flags & (DERIVED | ASYNC_DERIVED)) === 0) { + boundary = get_boundary(/** @type {Effect} */ (reaction)); + if (boundary) { + // @ts-ignore + var forks = boundary.fn.forks; + if (forks.has(signal) || forks.has(parent)) { + continue; + } + } + } + // Inspect effects need to run immediately, so that the stack trace makes sense if (DEV && (flags & INSPECT_EFFECT) !== 0) { inspect_effects.add(reaction); @@ -288,10 +310,76 @@ export function mark_reactions(signal, status) { // If the signal a) was previously clean or b) is an unowned derived, then mark it if ((flags & (CLEAN | UNOWNED)) !== 0) { if ((flags & DERIVED) !== 0) { - mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); + mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY, signal, only_boundary); } else { schedule_effect(/** @type {Effect} */ (reaction)); } } } } + +/** + * @param {Source | Derived} signal + * @param {any} forks + */ +function fork_dependencies(signal, forks) { + var entry = forks.get(signal); + if (entry === undefined) { + entry = { v: signal.v }; + forks.set(signal, entry); + if ((signal.f & DERIVED) !== 0) { + var deps = /** @type {Derived} */ (signal).deps; + if (deps !== null) { + for (var i = 0; i < deps.length; i++) { + fork_dependencies(deps[i], forks); + } + } + } + } +} + +/** + * @param {Value} signal + * @returns {void} + */ +function possibly_fork(signal) { + var reactions = signal.reactions; + if (reactions === null) return; + + var runes = is_runes(); + var length = reactions.length; + + for (var i = 0; i < length; i++) { + var reaction = reactions[i]; + var flags = reaction.f; + + // Skip any effects that are already dirty + if ((flags & DIRTY) !== 0) continue; + + // In legacy mode, skip the current effect to prevent infinite loops + if (!runes && reaction === active_effect) continue; + + // If the signal a) was previously clean or b) is an unowned derived, then mark it + if ((flags & (CLEAN | UNOWNED)) !== 0) { + if ((flags & DERIVED) !== 0) { + possibly_fork(/** @type {Derived} */ (reaction)); + } else { + if ((reaction.f & ASYNC_DERIVED) !== 0) { + // if (active_effect === signal) { + // set_signal_status(signal, MAYBE_DIRTY); + // return; + // } + var boundary = get_boundary(/** @type {Effect} */ (reaction)); + // @ts-ignore + var forks = boundary.fn.forks; + var deps = reaction.deps; + if (deps !== null) { + for (var s = 0; s < deps.length; s++) { + fork_dependencies(deps[s], forks); + } + } + } + } + } + } +} diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 008b94623e..6efa1dbe63 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -783,47 +783,11 @@ function flush_deferred() { } } -/** - * @param {Source | Derived} signal - * @param {any} forks - */ -function fork_dependencies(signal, forks) { - var entry = forks.get(signal); - if (entry === undefined) { - entry = { v: signal.v }; - forks.set(signal, entry); - if ((signal.f & DERIVED) !== 0) { - var deps = /** @type {Derived} */ (signal).deps; - if (deps !== null) { - for (var i = 0; i < deps.length; i++) { - fork_dependencies(deps[i], forks); - } - } - } - } -} - /** * @param {Effect} signal * @returns {void} */ export function schedule_effect(signal) { - if ((signal.f & ASYNC_DERIVED) !== 0) { - if (active_effect === signal) { - set_signal_status(signal, MAYBE_DIRTY); - return; - } - var boundary = get_boundary(signal); - // @ts-ignore - var forks = boundary.fn.forks; - var deps = signal.deps; - if (deps !== null) { - for (var i = 0; i < deps.length; i++) { - fork_dependencies(deps[i], forks); - } - } - } - if (scheduler_mode === FLUSH_MICROTASK) { if (!is_micro_task_queued) { is_micro_task_queued = true; @@ -1067,7 +1031,11 @@ export function get(signal) { var target_effect = event_boundary_effect ?? active_effect; - if (target_effect !== null && !is_flushing_async_derived) { + if ( + target_effect !== null && + !is_flushing_async_derived && + (target_effect.f & ASYNC_DERIVED) === 0 + ) { var boundary = get_boundary(target_effect); if (boundary !== null) { // @ts-ignore diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived/_config.js index 38ca6d34ad..dbe76c573b 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-derived/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-derived/_config.js @@ -32,11 +32,7 @@ export default test({ await Promise.resolve(); await Promise.resolve(); await Promise.resolve(); - await Promise.resolve(); - await Promise.resolve(); - await Promise.resolve(); await tick(); - flushSync(); assert.htmlEqual(target.innerHTML, '

84

'); d = deferred();