Skip to content

Commit 09c9a3c

Browse files
authored
fix: silence $inspect errors when the effect is about to be destroyed (#16391)
* fix: silence `$inspect` errors when the effect is about to be destroyed * changeset
1 parent 3edebd5 commit 09c9a3c

File tree

3 files changed

+39
-19
lines changed

3 files changed

+39
-19
lines changed

.changeset/six-swans-rush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: silence `$inspect` errors when the effect is about to be destroyed
Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UNINITIALIZED } from '../../../constants.js';
22
import { snapshot } from '../../shared/clone.js';
3-
import { inspect_effect, validate_effect } from '../reactivity/effects.js';
3+
import { inspect_effect, render_effect, validate_effect } from '../reactivity/effects.js';
44
import { untrack } from '../runtime.js';
55

66
/**
@@ -12,29 +12,44 @@ export function inspect(get_value, inspector = console.log) {
1212
validate_effect('$inspect');
1313

1414
let initial = true;
15+
let error = /** @type {any} */ (UNINITIALIZED);
1516

17+
// Inspect effects runs synchronously so that we can capture useful
18+
// stack traces. As a consequence, reading the value might result
19+
// in an error (an `$inspect(object.property)` will run before the
20+
// `{#if object}...{/if}` that contains it)
1621
inspect_effect(() => {
17-
/** @type {any} */
18-
var value = UNINITIALIZED;
19-
20-
// Capturing the value might result in an exception due to the inspect effect being
21-
// sync and thus operating on stale data. In the case we encounter an exception we
22-
// can bail-out of reporting the value. Instead we simply console.error the error
23-
// so at least it's known that an error occured, but we don't stop execution
2422
try {
25-
value = get_value();
26-
} catch (error) {
27-
// eslint-disable-next-line no-console
28-
console.error(error);
23+
var value = get_value();
24+
} catch (e) {
25+
error = e;
26+
return;
2927
}
3028

31-
if (value !== UNINITIALIZED) {
32-
var snap = snapshot(value, true);
33-
untrack(() => {
34-
inspector(initial ? 'init' : 'update', ...snap);
35-
});
36-
}
29+
var snap = snapshot(value, true);
30+
untrack(() => {
31+
inspector(initial ? 'init' : 'update', ...snap);
32+
});
3733

3834
initial = false;
3935
});
36+
37+
// If an error occurs, we store it (along with its stack trace).
38+
// If the render effect subsequently runs, we log the error,
39+
// but if it doesn't run it's because the `$inspect` was
40+
// destroyed, meaning we don't need to bother
41+
render_effect(() => {
42+
try {
43+
// call `get_value` so that this runs alongside the inspect effect
44+
get_value();
45+
} catch {
46+
// ignore
47+
}
48+
49+
if (error !== UNINITIALIZED) {
50+
// eslint-disable-next-line no-console
51+
console.error(error);
52+
error = UNINITIALIZED;
53+
}
54+
});
4055
}

packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default test({
1111
b1?.click();
1212
flushSync();
1313

14-
assert.ok(errors.length > 0);
14+
assert.equal(errors.length, 0);
1515
assert.deepEqual(logs, ['init', 'a', 'init', 'b']);
1616
}
1717
});

0 commit comments

Comments
 (0)