diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index cad7e22170..021a7148b8 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -461,6 +461,10 @@ export default class Block { this.add_variable(dispose); + this.event_listeners.forEach(event_listener => { + event_listener.arguments[2] = x`@attach_error_handler.call(${event_listener.arguments[0]}, #component, ${event_listener.arguments[2]})`; + }); + if (this.event_listeners.length === 1) { this.chunks.mount.push( b` diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index f74f4cdf1c..c8e41c55f3 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -329,7 +329,7 @@ export default function dom( const has_create_fragment = component.compile_options.dev || block.has_content(); if (has_create_fragment) { body.push(b` - function create_fragment(#ctx) { + function create_fragment(#ctx, #component) { ${block.get_contents()} } `); @@ -450,6 +450,15 @@ export default function dom( }) as Expression) }; + const instance_try_block: any = b` + try {} + catch(e) { + @handle_error($$self, e); + } + `; + + instance_try_block[0].block.body = instance_javascript.flat(); + body.push(b` function ${definition}(${args}) { ${injected.map(name => b`let ${name};`)} @@ -466,7 +475,7 @@ export default function dom( ${component.compile_options.dev && b`@validate_slots('${component.tag}', #slots, [${[...component.slots.keys()].map(key => `'${key}'`).join(',')}]);`} ${compute_slots} - ${instance_javascript} + ${instance_try_block} ${unknown_props_check} diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 8e12f9f0ee..054abe208a 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 e706928af3..f68eec4111 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -36,6 +36,7 @@ interface T$$ { context: Map; on_mount: any[]; on_destroy: any[]; + on_error: any[]; skip_bound: boolean; on_disconnect: any[]; root:Element|ShadowRoot @@ -104,6 +105,26 @@ function make_dirty(component, i) { component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); } +export function attach_error_handler(component, fn) { + return () => { + try { + fn.call(this, ...arguments); + } catch (e) { + handle_error(component, e); + } + } +} + +export function handle_error(component, e) { + if (component.$$.on_error.length === 0) { + throw e; + } + + component.$$.on_error.forEach(handler => { + handler(e); + }); +} + export function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) { const parent_component = current_component; set_current_component(component); @@ -122,6 +143,7 @@ export function init(component, options, instance, create_fragment, not_equal, p on_mount: [], on_destroy: [], on_disconnect: [], + on_error: [], before_update: [], after_update: [], context: new Map(parent_component ? parent_component.$$.context : options.context || []), @@ -153,7 +175,7 @@ export function init(component, options, instance, create_fragment, not_equal, p run_all($$.before_update); // `false` as a special case of no DOM component - $$.fragment = create_fragment ? create_fragment($$.ctx) : false; + $$.fragment = create_fragment ? create_fragment($$.ctx, component) : false; if (options.target) { if (options.hydrate) { diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index bb3df3d295..e69249faa7 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -27,6 +27,10 @@ export function onDestroy(fn: () => any) { get_current_component().$$.on_destroy.push(fn); } +export function onError(fn: () => any) { + get_current_component().$$.on_error.push(fn); +} + export function createEventDispatcher< EventMap extends {} = any >(): >(type: EventKey, detail?: EventMap[EventKey]) => void {