diff --git a/.changeset/fix-if-block-boundary-onerror.md b/.changeset/fix-if-block-boundary-onerror.md new file mode 100644 index 0000000000..608dce6814 --- /dev/null +++ b/.changeset/fix-if-block-boundary-onerror.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: run boundary `onerror` callbacks in a microtask, in case they result in the boundary's destruction diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 90a695551a..ef5f0e116d 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -1,5 +1,6 @@ /** @import { Effect, Source, TemplateNode, } from '#client' */ import { + BLOCK_EFFECT, BOUNDARY_EFFECT, COMMENT_NODE, DIRTY, @@ -449,21 +450,16 @@ export class Boundary { } }; - var previous_reaction = active_reaction; - - try { - set_active_reaction(null); - calling_on_error = true; - onerror?.(error, reset); - calling_on_error = false; - } catch (error) { - invoke_error_boundary(error, this.#effect && this.#effect.parent); - } finally { - set_active_reaction(previous_reaction); - } + queue_micro_task(() => { + try { + calling_on_error = true; + onerror?.(error, reset); + calling_on_error = false; + } catch (error) { + invoke_error_boundary(error, this.#effect && this.#effect.parent); + } - if (failed) { - queue_micro_task(() => { + if (failed) { this.#failed_effect = this.#run(() => { Batch.ensure(); this.#is_creating_fallback = true; @@ -483,8 +479,8 @@ export class Boundary { this.#is_creating_fallback = false; } }); - }); - } + } + }); } } diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-24/Child.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-24/Child.svelte new file mode 100644 index 0000000000..cd25ece1b5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-24/Child.svelte @@ -0,0 +1,5 @@ + + +
Child content
diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-24/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-24/_config.js new file mode 100644 index 0000000000..b5b5f296d8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-24/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + test({ assert, target }) { + flushSync(); + + // When exception is set by onerror, the {#if !exception} block should hide + // and only the {#if exception} block should be visible + assert.htmlEqual(target.innerHTML, 'caught error: child error
'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-24/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-24/main.svelte new file mode 100644 index 0000000000..5116580003 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-24/main.svelte @@ -0,0 +1,19 @@ + + +{#if !exception} +condition is {String(!exception)}
+caught error: {exception.message}
+{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-25/Child.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-25/Child.svelte new file mode 100644 index 0000000000..3d5770fc61 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-25/Child.svelte @@ -0,0 +1,8 @@ + + ++ boom +
diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-25/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-25/_config.js new file mode 100644 index 0000000000..b5b5f296d8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-25/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + test({ assert, target }) { + flushSync(); + + // When exception is set by onerror, the {#if !exception} block should hide + // and only the {#if exception} block should be visible + assert.htmlEqual(target.innerHTML, 'caught error: child error
'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-25/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-25/main.svelte new file mode 100644 index 0000000000..243d10cf3a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-25/main.svelte @@ -0,0 +1,19 @@ + + +{#if !exception} +condition is {String(!exception)}
+caught error: {exception}
+{/if}