From e1246e1bc32cf62dfe8aaac7f15aff5dae83525d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Jul 2025 09:19:15 -0400 Subject: [PATCH 1/2] fix: silence `$inspect` errors when the effect is about to be destroyed --- .../svelte/src/internal/client/dev/inspect.js | 51 ++++++++++++------- .../samples/inspect-exception/_config.js | 2 +- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/packages/svelte/src/internal/client/dev/inspect.js b/packages/svelte/src/internal/client/dev/inspect.js index e15c66901c5f..c593f2622ca8 100644 --- a/packages/svelte/src/internal/client/dev/inspect.js +++ b/packages/svelte/src/internal/client/dev/inspect.js @@ -1,6 +1,6 @@ import { UNINITIALIZED } from '../../../constants.js'; import { snapshot } from '../../shared/clone.js'; -import { inspect_effect, validate_effect } from '../reactivity/effects.js'; +import { inspect_effect, render_effect, validate_effect } from '../reactivity/effects.js'; import { untrack } from '../runtime.js'; /** @@ -12,29 +12,44 @@ export function inspect(get_value, inspector = console.log) { validate_effect('$inspect'); let initial = true; + let error = /** @type {any} */ (UNINITIALIZED); + // Inspect effects runs synchronously so that we can capture useful + // stack traces. As a consequence, reading the value might result + // in an error (an `$inspect(object.property)` will run before the + // `{#if object}...{/if}` that contains it) inspect_effect(() => { - /** @type {any} */ - var value = UNINITIALIZED; - - // Capturing the value might result in an exception due to the inspect effect being - // sync and thus operating on stale data. In the case we encounter an exception we - // can bail-out of reporting the value. Instead we simply console.error the error - // so at least it's known that an error occured, but we don't stop execution try { - value = get_value(); - } catch (error) { - // eslint-disable-next-line no-console - console.error(error); + var value = get_value(); + } catch (e) { + error = e; + return; } - if (value !== UNINITIALIZED) { - var snap = snapshot(value, true); - untrack(() => { - inspector(initial ? 'init' : 'update', ...snap); - }); - } + var snap = snapshot(value, true); + untrack(() => { + inspector(initial ? 'init' : 'update', ...snap); + }); initial = false; }); + + // If an error occurs, we store it (along with its stack trace). + // If the render effect subsequently runs, we log the error, + // but if it doesn't run it's because the `$inspect` was + // destroyed, meaning we don't need to bother + render_effect(() => { + try { + // call `get_value` so that this runs alongside the inspect effect + get_value(); + } catch { + // ignore + } + + if (error !== UNINITIALIZED) { + // eslint-disable-next-line no-console + console.error(error); + error = UNINITIALIZED; + } + }); } diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js index e155ff236a48..83e0eb944370 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js @@ -11,7 +11,7 @@ export default test({ b1?.click(); flushSync(); - assert.ok(errors.length > 0); + assert.equal(errors.length, 0); assert.deepEqual(logs, ['init', 'a', 'init', 'b']); } }); From 8d6875fe5a1078f295d38f6040ad57bae50e0396 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Jul 2025 09:19:49 -0400 Subject: [PATCH 2/2] changeset --- .changeset/six-swans-rush.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/six-swans-rush.md diff --git a/.changeset/six-swans-rush.md b/.changeset/six-swans-rush.md new file mode 100644 index 000000000000..cfb5b9740082 --- /dev/null +++ b/.changeset/six-swans-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: silence `$inspect` errors when the effect is about to be destroyed