diff --git a/src/runtime/index.ts b/src/runtime/index.ts index e6c0c916f23e..735dd2873527 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -3,6 +3,7 @@ import './ambient'; export { onMount, onDestroy, + onError, beforeUpdate, afterUpdate, setContext, diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 0ce3b3209bb6..a9ac96db1263 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -34,6 +34,7 @@ interface T$$ { context: Map; on_mount: any[]; on_destroy: any[]; + on_error: any[]; } export function bind(component, name, callback) { @@ -114,6 +115,7 @@ export function init(component, options, instance, create_fragment, not_equal, p // lifecycle on_mount: [], + on_error: [], on_destroy: [], before_update: [], after_update: [], diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index a8e37e9632a3..5a716d3d95c2 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -27,6 +27,10 @@ export function onDestroy(fn) { get_current_component().$$.on_destroy.push(fn); } +export function onError(fn) { + get_current_component().$$.on_error.push(fn); +} + export function createEventDispatcher() { const component = get_current_component(); diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index 7cb00c085bbc..350105efad9f 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -70,12 +70,36 @@ export function flush() { } function update($$) { - if ($$.fragment !== null) { - $$.update($$.dirty); - run_all($$.before_update); - $$.fragment && $$.fragment.p($$.dirty, $$.ctx); - $$.dirty = null; - - $$.after_update.forEach(add_render_callback); + if ($$.fragment) { + if ($$.on_error.length == 0) { + exec_update($$); + } else { + try_exec_update($$); + } } } + +function exec_update($$) { + $$.update($$.dirty); + run_all($$.before_update); + $$.fragment && $$.fragment.p($$.dirty, $$.ctx); + $$.dirty = null; + + $$.after_update.forEach(add_render_callback); +} + +function try_exec_update($$) { + try { + exec_update($$); + } catch (e) { + let handled = false; + for (let i = 0; i < $$.on_error.length; i += 1) { + const callback = $$.on_error[i]; + const result = callback(e); + if (result !== false) { + handled = true; + } + } + if (!handled) throw e; + } +} \ No newline at end of file diff --git a/src/runtime/internal/ssr.ts b/src/runtime/internal/ssr.ts index 274006f24313..39570325335e 100644 --- a/src/runtime/internal/ssr.ts +++ b/src/runtime/internal/ssr.ts @@ -85,6 +85,7 @@ export function create_ssr_component(fn) { // these will be immediately discarded on_mount: [], + on_error: [], before_update: [], after_update: [], callbacks: blank_object() diff --git a/test/runtime/samples/onerror-fires-when-error/_config.js b/test/runtime/samples/onerror-fires-when-error/_config.js new file mode 100644 index 000000000000..85b0acdd26f7 --- /dev/null +++ b/test/runtime/samples/onerror-fires-when-error/_config.js @@ -0,0 +1,7 @@ +export default { + test({ assert, target }) { + const div = target.querySelector('div'); + + assert.equal('error', div.className); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/onerror-fires-when-error/container.js b/test/runtime/samples/onerror-fires-when-error/container.js new file mode 100644 index 000000000000..7c645e42fb75 --- /dev/null +++ b/test/runtime/samples/onerror-fires-when-error/container.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/test/runtime/samples/onerror-fires-when-error/main.svelte b/test/runtime/samples/onerror-fires-when-error/main.svelte new file mode 100644 index 000000000000..638f43091f62 --- /dev/null +++ b/test/runtime/samples/onerror-fires-when-error/main.svelte @@ -0,0 +1,16 @@ + + +{#if error} +
{error.message}
+{:else} +
{getWidget()}
+{/if}