Skip to content

Commit b558852

Browse files
authored
fix: capture infinite_loop_guard in error boundary (#14534)
* fix: capture infinite_loop_guard in error boundary * fix
1 parent d5a28a0 commit b558852

File tree

5 files changed

+85
-12
lines changed

5 files changed

+85
-12
lines changed

.changeset/hip-forks-exercise.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: capture infinite_loop_guard in error boundary

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

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ let scheduler_mode = FLUSH_MICROTASK;
4848
// Used for handling scheduling
4949
let is_micro_task_queued = false;
5050

51+
/** @type {Effect | null} */
52+
let last_scheduled_effect = null;
53+
5154
export let is_flushing_effect = false;
5255
export let is_destroying_effect = false;
5356

@@ -532,27 +535,47 @@ export function update_effect(effect) {
532535
}
533536
}
534537

538+
function log_effect_stack() {
539+
// eslint-disable-next-line no-console
540+
console.error(
541+
'Last ten effects were: ',
542+
dev_effect_stack.slice(-10).map((d) => d.fn)
543+
);
544+
dev_effect_stack = [];
545+
}
546+
535547
function infinite_loop_guard() {
536548
if (flush_count > 1000) {
537549
flush_count = 0;
538-
if (DEV) {
539-
try {
540-
e.effect_update_depth_exceeded();
541-
} catch (error) {
550+
try {
551+
e.effect_update_depth_exceeded();
552+
} catch (error) {
553+
if (DEV) {
542554
// stack is garbage, ignore. Instead add a console.error message.
543555
define_property(error, 'stack', {
544556
value: ''
545557
});
546-
// eslint-disable-next-line no-console
547-
console.error(
548-
'Last ten effects were: ',
549-
dev_effect_stack.slice(-10).map((d) => d.fn)
550-
);
551-
dev_effect_stack = [];
558+
}
559+
// Try and handle the error so it can be caught at a boundary, that's
560+
// if there's an effect available from when it was last scheduled
561+
if (last_scheduled_effect !== null) {
562+
if (DEV) {
563+
try {
564+
handle_error(error, last_scheduled_effect, null, null);
565+
} catch (e) {
566+
// Only log the effect stack if the error is re-thrown
567+
log_effect_stack();
568+
throw e;
569+
}
570+
} else {
571+
handle_error(error, last_scheduled_effect, null, null);
572+
}
573+
} else {
574+
if (DEV) {
575+
log_effect_stack();
576+
}
552577
throw error;
553578
}
554-
} else {
555-
e.effect_update_depth_exceeded();
556579
}
557580
}
558581
flush_count++;
@@ -637,8 +660,10 @@ function process_deferred() {
637660
const previous_queued_root_effects = queued_root_effects;
638661
queued_root_effects = [];
639662
flush_queued_root_effects(previous_queued_root_effects);
663+
640664
if (!is_micro_task_queued) {
641665
flush_count = 0;
666+
last_scheduled_effect = null;
642667
if (DEV) {
643668
dev_effect_stack = [];
644669
}
@@ -657,6 +682,8 @@ export function schedule_effect(signal) {
657682
}
658683
}
659684

685+
last_scheduled_effect = signal;
686+
660687
var effect = signal;
661688

662689
while (effect.parent !== null) {
@@ -776,6 +803,7 @@ export function flush_sync(fn) {
776803
}
777804

778805
flush_count = 0;
806+
last_scheduled_effect = null;
779807
if (DEV) {
780808
dev_effect_stack = [];
781809
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
let count = $state(0);
3+
let clicked = $state(false);
4+
5+
function increment() {
6+
clicked = true;
7+
count++;
8+
}
9+
10+
$effect(() => {
11+
if (clicked) {
12+
count++;
13+
}
14+
});
15+
</script>
16+
17+
<button onclick={increment}>{count}</button>
Lines changed: 13 additions & 0 deletions
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 }) {
6+
let btn = target.querySelector('button');
7+
8+
btn?.click();
9+
flushSync();
10+
11+
assert.htmlEqual(target.innerHTML, `<div class="error">An error occurred!</div>`);
12+
}
13+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
import Child from "./Child.svelte"
3+
</script>
4+
<svelte:boundary>
5+
<Child />
6+
7+
{#snippet failed()}
8+
<div class="error">An error occurred!</div>
9+
{/snippet}
10+
</svelte:boundary>

0 commit comments

Comments
 (0)