pull/16091/head
Rich Harris 4 months ago
parent 7d72187fed
commit 34c2900a64

@ -15,37 +15,25 @@ export function reset_is_throwing_error() {
is_throwing_error = false; is_throwing_error = false;
} }
/** @type {null | unknown} */
let current_error = null;
/** /**
* @param {unknown} error * @param {unknown} error
* @param {Effect} effect * @param {Effect} effect
* @param {Effect | null} [previous_effect] * @param {Effect | null} [previous_effect]
*/ */
export function handle_error(error, effect, previous_effect = null) { export function handle_error(error, effect, previous_effect = null) {
if (is_throwing_error) { if (error === current_error) {
if (previous_effect === null) { // TODO is this necessary?
is_throwing_error = false;
}
if (should_rethrow_error(effect)) {
throw error;
}
return; return;
} }
if (previous_effect !== null) {
is_throwing_error = true;
}
if (DEV && error instanceof Error) { if (DEV && error instanceof Error) {
adjust_error(error, effect); adjust_error(error, effect);
} }
invoke_error_boundary(error, effect); invoke_error_boundary(error, effect);
if (should_rethrow_error(effect)) {
throw error;
}
} }
/** /**

@ -343,6 +343,13 @@ export function update_reaction(reaction) {
} }
return result; return result;
} catch (error) {
var effect = get_effect(reaction);
if (effect) {
handle_error(error, effect);
} else {
throw error;
}
} finally { } finally {
new_deps = previous_deps; new_deps = previous_deps;
skipped_deps = previous_skipped_deps; skipped_deps = previous_skipped_deps;
@ -357,6 +364,18 @@ export function update_reaction(reaction) {
} }
} }
/** @param {Reaction} reaction */
function get_effect(reaction) {
/** @type {Reaction | null;} */
var r = reaction;
while (r !== null && (r.f & DERIVED) !== 0) {
r = /** @type {Derived} */ (r).parent;
}
return /** @type {Effect | null} */ (r);
}
/** /**
* @template V * @template V
* @param {Reaction} signal * @param {Reaction} signal
@ -470,8 +489,6 @@ export function update_effect(effect) {
if (DEV) { if (DEV) {
dev_effect_stack.push(effect); dev_effect_stack.push(effect);
} }
} catch (error) {
handle_error(error, effect, previous_effect);
} finally { } finally {
is_updating_effect = was_updating_effect; is_updating_effect = was_updating_effect;
active_effect = previous_effect; active_effect = previous_effect;
@ -570,27 +587,23 @@ function flush_queued_effects(effects) {
var effect = effects[i]; var effect = effects[i];
if ((effect.f & (DESTROYED | INERT)) === 0) { if ((effect.f & (DESTROYED | INERT)) === 0) {
try { if (check_dirtiness(effect)) {
if (check_dirtiness(effect)) { update_effect(effect);
update_effect(effect);
// Effects with no dependencies or teardown do not get added to the effect tree.
// Effects with no dependencies or teardown do not get added to the effect tree. // Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we
// Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we // don't know if we need to keep them until they are executed. Doing the check
// don't know if we need to keep them until they are executed. Doing the check // here (rather than in `update_effect`) allows us to skip the work for
// here (rather than in `update_effect`) allows us to skip the work for // immediate effects.
// immediate effects. if (effect.deps === null && effect.first === null && effect.nodes_start === null) {
if (effect.deps === null && effect.first === null && effect.nodes_start === null) { if (effect.teardown === null) {
if (effect.teardown === null) { // remove this effect from the graph
// remove this effect from the graph unlink_effect(effect);
unlink_effect(effect); } else {
} else { // keep the effect in the graph, but free up some memory
// keep the effect in the graph, but free up some memory effect.fn = null;
effect.fn = null;
}
} }
} }
} catch (error) {
handle_error(error, effect);
} }
} }
} }
@ -649,12 +662,8 @@ function process_effects(root) {
} else if (is_branch) { } else if (is_branch) {
effect.f ^= CLEAN; effect.f ^= CLEAN;
} else { } else {
try { if (check_dirtiness(effect)) {
if (check_dirtiness(effect)) { update_effect(effect);
update_effect(effect);
}
} catch (error) {
handle_error(error, effect);
} }
} }

@ -5,9 +5,8 @@ export default test({
test({ assert, target }) { test({ assert, target }) {
const btn = target.querySelector('button'); const btn = target.querySelector('button');
btn?.click(); assert.throws(() => {
flushSync(); flushSync(() => btn?.click());
}, /kaboom/);
assert.htmlEqual(target.innerHTML, `<button>change</button><p>Error occured</p>`);
} }
}); });

@ -1,20 +1,18 @@
<script> <script>
let count = $state(0); let count = $state(0);
const things = $derived.by(() => { const d = $derived.by(() => {
if (count === 1) { if (count === 1) {
throw new Error('123') throw new Error('kaboom')
} }
return [1, 2 ,3] return count
}) })
</script> </script>
<button onclick={() => count++}>change</button> <button onclick={() => count++}>change</button>
<svelte:boundary> <svelte:boundary>
{#each things as thing} {d}
<p>{thing}</p>
{/each}
{#snippet failed()} {#snippet failed()}
<p>Error occured</p> <p>Error occured</p>

@ -1,7 +1,14 @@
<script> <script>
const { things } = $props(); const { count } = $props();
const d = $derived.by(() => {
if (count === 1) {
throw new Error('kaboom')
}
return count
});
$effect(() => { $effect(() => {
things d;
}) });
</script> </script>

@ -8,6 +8,6 @@ export default test({
btn?.click(); btn?.click();
flushSync(); flushSync();
assert.htmlEqual(target.innerHTML, `<button>change</button><p>Error occured</p>`); assert.htmlEqual(target.innerHTML, `<button>change</button><p>Error occurred</p>`);
} }
}); });

@ -2,21 +2,14 @@
import Child from './Child.svelte'; import Child from './Child.svelte';
let count = $state(0); let count = $state(0);
const things = $derived.by(() => {
if (count === 1) {
throw new Error('123')
}
return [1, 2 ,3]
})
</script> </script>
<button onclick={() => count++}>change</button> <button onclick={() => count++}>change</button>
<svelte:boundary> <svelte:boundary>
<Child {things} /> <Child {count} />
{#snippet failed()} {#snippet failed()}
<p>Error occured</p> <p>Error occurred</p>
{/snippet} {/snippet}
</svelte:boundary> </svelte:boundary>

@ -5,11 +5,11 @@ export default test({
test({ assert, target }) { test({ assert, target }) {
let btn = target.querySelector('button'); let btn = target.querySelector('button');
btn?.click();
btn?.click();
assert.throws(() => { assert.throws(() => {
flushSync(); flushSync(() => {
btn?.click();
btn?.click();
});
}, /test\n\n\tin {expression}\n/); }, /test\n\n\tin {expression}\n/);
} }
}); });

@ -1,16 +1,18 @@
<script> <script>
let count = $state(0); let count = $state(0);
let test = $derived.by(() => { function maybe_throw() {
if (count > 1) { if (count > 1) {
throw new Error('test'); throw new Error('test');
} }
});
return count;
}
</script> </script>
<svelte:boundary onerror={(e) => { throw(e) }}> <svelte:boundary onerror={(e) => { throw(e) }}>
<div>Count: {count}</div> <div>Count: {count}</div>
<button onclick={() => count++}>Increment</button> <button onclick={() => count++}>Increment</button>
{count} / {test} {count} / {maybe_throw()}
</svelte:boundary> </svelte:boundary>

Loading…
Cancel
Save