From 69b196d605b3cb90fc8a6b862c83282ce0fe44d2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 25 Feb 2026 09:41:54 -0500 Subject: [PATCH] fix: reinstate reactivity loss tracking --- .changeset/twelve-stars-serve.md | 5 ++++ .../src/internal/client/reactivity/async.js | 12 ++++----- .../internal/client/reactivity/deriveds.js | 13 +++++---- .../svelte/src/internal/client/runtime.js | 27 +++++++++---------- 4 files changed, 30 insertions(+), 27 deletions(-) create mode 100644 .changeset/twelve-stars-serve.md diff --git a/.changeset/twelve-stars-serve.md b/.changeset/twelve-stars-serve.md new file mode 100644 index 0000000000..6a4d7ecfa7 --- /dev/null +++ b/.changeset/twelve-stars-serve.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: reinstate reactivity loss tracking diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index 31408ee8f9..3431d0ffa5 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -19,10 +19,10 @@ import { import { Batch, current_batch } from './batch.js'; import { async_derived, - current_async_effect, + active_async_effect, derived, derived_safe_equal, - set_from_async_derived + set_active_async_effect } from './deriveds.js'; import { aborted } from './effects.js'; @@ -120,7 +120,7 @@ export function capture() { if (activate_batch) previous_batch?.activate(); if (DEV) { - set_from_async_derived(null); + set_active_async_effect(null); set_dev_stack(previous_dev_stack); } }; @@ -152,11 +152,11 @@ export async function save(promise) { * @returns {Promise<() => T>} */ export async function track_reactivity_loss(promise) { - var previous_async_effect = current_async_effect; + var previous_async_effect = active_async_effect; var value = await promise; return () => { - set_from_async_derived(previous_async_effect); + set_active_async_effect(previous_async_effect); return value; }; } @@ -213,7 +213,7 @@ export function unset_context(deactivate_batch = true) { if (deactivate_batch) current_batch?.deactivate(); if (DEV) { - set_from_async_derived(null); + set_active_async_effect(null); set_dev_stack(null); } } diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 7df7651294..272c85aad5 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -36,7 +36,6 @@ import { import { eager_effects, internal_set, set_eager_effects, source } from './sources.js'; import { get_error } from '../../shared/dev.js'; import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js'; -import { Boundary } from '../dom/blocks/boundary.js'; import { component_context } from '../context.js'; import { UNINITIALIZED } from '../../../constants.js'; import { batch_values, current_batch } from './batch.js'; @@ -45,11 +44,11 @@ import { deferred, includes, noop } from '../../shared/utils.js'; import { set_signal_status, update_derived_status } from './status.js'; /** @type {Effect | null} */ -export let current_async_effect = null; +export let active_async_effect = null; /** @param {Effect | null} v */ -export function set_from_async_derived(v) { - current_async_effect = v; +export function set_active_async_effect(v) { + active_async_effect = v; } export const recent_async_deriveds = new Set(); @@ -123,7 +122,7 @@ export function async_derived(fn, label, location) { var deferreds = new Map(); async_effect(() => { - if (DEV) current_async_effect = active_effect; + if (DEV) active_async_effect = active_effect; /** @type {ReturnType>} */ var d = deferred(); @@ -139,7 +138,7 @@ export function async_derived(fn, label, location) { unset_context(); } - if (DEV) current_async_effect = null; + if (DEV) active_async_effect = null; var batch = /** @type {Batch} */ (current_batch); @@ -156,7 +155,7 @@ export function async_derived(fn, label, location) { * @param {unknown} error */ const handler = (value, error = undefined) => { - current_async_effect = null; + if (DEV) active_async_effect = null; batch.activate(); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 3260c24b8c..79e0f0991d 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -27,7 +27,7 @@ import { } from './constants.js'; import { old_values } from './reactivity/sources.js'; import { - destroy_derived_effects, + active_async_effect, execute_derived, freeze_derived_effects, recent_async_deriveds, @@ -58,6 +58,7 @@ import { UNINITIALIZED } from '../../constants.js'; import { captured_signals } from './legacy.js'; import { without_reactive_context } from './dom/elements/bindings/shared.js'; import { set_signal_status, update_derived_status } from './reactivity/status.js'; +import * as w from './warnings.js'; let is_updating_effect = false; @@ -568,19 +569,17 @@ export function get(signal) { } if (DEV) { - // TODO reinstate this, but make it actually work - // if (current_async_effect) { - // var tracking = (current_async_effect.f & REACTION_IS_UPDATING) !== 0; - // var was_read = current_async_effect.deps?.includes(signal); - - // if (!tracking && !untracking && !was_read) { - // w.await_reactivity_loss(/** @type {string} */ (signal.label)); + if ( + !untracking && + active_async_effect && + (active_async_effect.f & REACTION_IS_UPDATING) === 0 + ) { + w.await_reactivity_loss(/** @type {string} */ (signal.label)); - // var trace = get_error('traced at'); - // // eslint-disable-next-line no-console - // if (trace) console.warn(trace); - // } - // } + var trace = get_error('traced at'); + // eslint-disable-next-line no-console + if (trace) console.warn(trace); + } recent_async_deriveds.delete(signal); @@ -595,7 +594,7 @@ export function get(signal) { if (signal.trace) { signal.trace(); } else { - var trace = get_error('traced at'); + trace = get_error('traced at'); if (trace) { var entry = tracing_expressions.entries.get(signal);