Skip to content

Commit ce5efe4

Browse files
committed
tweak
tweak tweak tweak more fixes tweak tweak more fixes changeset
1 parent 058e62a commit ce5efe4

File tree

9 files changed

+117
-29
lines changed

9 files changed

+117
-29
lines changed

.changeset/polite-peaches-do.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
feat: adds error boundaries

packages/svelte/src/internal/client/dom/blocks/boundary.js

+1-9
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,9 @@ export function boundary(node, boundary_fn, props) {
5050

5151
block(() => {
5252
var boundary = /** @type {Effect} */ (active_effect);
53-
var start = hydrate_node;
5453

5554
// We re-use the effect's fn property to avoid allocation of an additional field
56-
boundary.fn = (/** @type {{ error?: Error }} */ payload) => {
57-
let { error } = payload;
58-
59-
// In the future, boundaries might handle other things other than errors
60-
if (!error) {
61-
return;
62-
}
63-
55+
boundary.fn = (/** @type { Error }} */ error) => {
6456
var onerror = props.onerror;
6557
let failed_snippet = props.failed;
6658

packages/svelte/src/internal/client/runtime.js

+33-19
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,10 @@ function propagate_error(error, effect) {
243243
if ((current.f & BOUNDARY_EFFECT) !== 0) {
244244
try {
245245
// @ts-ignore
246-
current.fn({ error });
246+
current.fn(error);
247247
} catch {
248+
// Remove boundary flag from effect
249+
current.f ^= BOUNDARY_EFFECT;
248250
current = parent;
249251
continue;
250252
}
@@ -485,7 +487,7 @@ export function update_effect(effect) {
485487
dev_effect_stack.push(effect);
486488
}
487489
} catch (error) {
488-
handle_error(/** @type {Error} */ (error), effect, previous_component_context);
490+
handle_error(/** @type {Error} */ (error), effect, previous_component_context || effect.ctx);
489491
} finally {
490492
active_effect = previous_effect;
491493

@@ -565,22 +567,28 @@ function flush_queued_effects(effects) {
565567
for (var i = 0; i < length; i++) {
566568
var effect = effects[i];
567569

568-
if ((effect.f & (DESTROYED | INERT)) === 0 && check_dirtiness(effect)) {
569-
update_effect(effect);
570-
571-
// Effects with no dependencies or teardown do not get added to the effect tree.
572-
// Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we
573-
// don't know if we need to keep them until they are executed. Doing the check
574-
// here (rather than in `update_effect`) allows us to skip the work for
575-
// immediate effects.
576-
if (effect.deps === null && effect.first === null && effect.nodes_start === null) {
577-
if (effect.teardown === null) {
578-
// remove this effect from the graph
579-
unlink_effect(effect);
580-
} else {
581-
// keep the effect in the graph, but free up some memory
582-
effect.fn = null;
570+
if ((effect.f & (DESTROYED | INERT)) === 0) {
571+
try {
572+
if (check_dirtiness(effect)) {
573+
update_effect(effect);
574+
575+
// Effects with no dependencies or teardown do not get added to the effect tree.
576+
// Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we
577+
// don't know if we need to keep them until they are executed. Doing the check
578+
// here (rather than in `update_effect`) allows us to skip the work for
579+
// immediate effects.
580+
if (effect.deps === null && effect.first === null && effect.nodes_start === null) {
581+
if (effect.teardown === null) {
582+
// remove this effect from the graph
583+
unlink_effect(effect);
584+
} else {
585+
// keep the effect in the graph, but free up some memory
586+
effect.fn = null;
587+
}
588+
}
583589
}
590+
} catch (error) {
591+
handle_error(/** @type {Error} */ (error), effect, effect.ctx);
584592
}
585593
}
586594
}
@@ -653,8 +661,14 @@ function process_effects(effect, collected_effects) {
653661
if ((flags & RENDER_EFFECT) !== 0) {
654662
if (is_branch) {
655663
current_effect.f ^= CLEAN;
656-
} else if (check_dirtiness(current_effect)) {
657-
update_effect(current_effect);
664+
} else {
665+
try {
666+
if (check_dirtiness(current_effect)) {
667+
update_effect(current_effect);
668+
}
669+
} catch (error) {
670+
handle_error(/** @type {Error} */ (error), current_effect, current_effect.ctx);
671+
}
658672
}
659673

660674
var child = current_effect.first;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
test({ assert, target, logs }) {
6+
const btn = target.querySelector('button');
7+
8+
btn?.click();
9+
flushSync();
10+
11+
assert.htmlEqual(target.innerHTML, `<button>change</button><p>Error occured</p>`);
12+
}
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script>
2+
let count = $state(0);
3+
4+
const things = $derived.by(() => {
5+
if (count === 1) {
6+
throw new Error('123')
7+
}
8+
return [1, 2 ,3]
9+
})
10+
</script>
11+
12+
<button onclick={() => count++}>change</button>
13+
14+
<svelte:boundary>
15+
{#each things as thing}
16+
<p>{thing}</p>
17+
{/each}
18+
19+
{#snippet failed()}
20+
<p>Error occured</p>
21+
{/snippet}
22+
</svelte:boundary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
const { things } = $props();
3+
4+
$effect(() => {
5+
things
6+
})
7+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
test({ assert, target, logs }) {
6+
const btn = target.querySelector('button');
7+
8+
btn?.click();
9+
flushSync();
10+
11+
assert.htmlEqual(target.innerHTML, `<button>change</button><p>Error occured</p>`);
12+
}
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
4+
let count = $state(0);
5+
6+
const things = $derived.by(() => {
7+
if (count === 1) {
8+
throw new Error('123')
9+
}
10+
return [1, 2 ,3]
11+
})
12+
</script>
13+
14+
<button onclick={() => count++}>change</button>
15+
16+
<svelte:boundary>
17+
<Child {things} />
18+
19+
{#snippet failed()}
20+
<p>Error occured</p>
21+
{/snippet}
22+
</svelte:boundary>

packages/svelte/tests/runtime-runes/samples/error-boundary-5/main.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</svelte:boundary>
1111

1212
{#snippet failed(e, retry)}
13-
<div>{e.message}</div>
13+
<div>too high</div>
1414
<button onclick={retry}>Retry</button>
1515
{/snippet}
1616
</svelte:boundary>

0 commit comments

Comments
 (0)