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
pull/16406/head
Rich Harris 2 months ago committed by GitHub
parent 3edebd5103
commit 09c9a3c165
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: silence `$inspect` errors when the effect is about to be destroyed

@ -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;
}
});
}

@ -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']);
}
});

Loading…
Cancel
Save