Skip to content

Commit 5d2780a

Browse files
authored
feat(): add setErrorHandler() (#2704)
1 parent 99ef518 commit 5d2780a

File tree

9 files changed

+33
-14
lines changed

9 files changed

+33
-14
lines changed

src/client/client-log.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import type * as d from '../declarations';
12
import { BUILD } from '@app-data';
23

4+
export let consoleError: d.ErrorHandler = (e: any, _?: any) => console.error(e);
5+
36
export const STENCIL_DEV_MODE = BUILD.isTesting
47
? ['STENCIL:'] // E2E testing
58
: ['%cstencil', 'color: white;background:#4c47ff;font-weight: bold; font-size:10px; padding:2px 6px; border-radius: 5px'];
@@ -10,4 +13,4 @@ export const consoleDevWarn = (...m: any[]) => console.warn(...STENCIL_DEV_MODE,
1013

1114
export const consoleDevInfo = (...m: any[]) => console.info(...STENCIL_DEV_MODE, ...m);
1215

13-
export const consoleError = (e: any) => console.error(e);
16+
export const setErrorHandler = (handler: d.ErrorHandler) => consoleError = handler;

src/compiler/transformers/update-stencil-core-import.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ const KEEP_IMPORTS = new Set([
7070
'forceUpdate',
7171
'getRenderingRef',
7272
'forceModeUpdate',
73+
'setErrorHandler'
7374
]);

src/declarations/stencil-public-runtime.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ export declare const Watch: WatchDecorator;
245245

246246
export type ResolutionHandler = (elm: HTMLElement) => string | undefined | null;
247247

248+
export type ErrorHandler = (err: any, element?: HTMLElement) => void;
249+
248250
/**
249251
* `setMode()` is used for libraries which provide multiple "modes" for styles.
250252
*/
@@ -312,6 +314,13 @@ export declare function writeTask(task: RafCallback): void;
312314
* For further information: https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing
313315
*/
314316
export declare function readTask(task: RafCallback): void;
317+
318+
/**
319+
* `setErrorHandler()` can be used to inject a custom global error handler.
320+
* Unhandled exception raised while rendering, during event handling, or lifecycles will trigger the custom event handler.
321+
*/
322+
export declare const setErrorHandler: (handler: ErrorHandler) => void;
323+
315324
/**
316325
* This file gets copied to all distributions of stencil component collections.
317326
* - no imports

src/internal/stencil-core/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export {
4444
State,
4545
Watch,
4646
writeTask,
47+
setErrorHandler,
4748
} from '../stencil-public-runtime';
4849

4950
export type { StencilConfig as Config, PrerenderConfig } from '../stencil-public-compiler';

src/internal/stencil-core/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ export {
1111
setMode,
1212
setAssetPath,
1313
writeTask,
14+
setErrorHandler,
1415
} from '../client/index.js';

src/runtime/host-listener.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type * as d from '../declarations';
22
import { BUILD } from '@app-data';
3-
import { doc, plt, supportsListenerOptions, win } from '@platform';
3+
import { doc, plt, consoleError, supportsListenerOptions, win } from '@platform';
44
import { HOST_FLAGS, LISTENER_FLAGS } from '@utils';
55

66
export const addHostEventListeners = (elm: d.HostElement, hostRef: d.HostRef, listeners: d.ComponentRuntimeHostListener[], attachParentListeners: boolean) => {
@@ -36,15 +36,19 @@ export const addHostEventListeners = (elm: d.HostElement, hostRef: d.HostRef, li
3636
};
3737

3838
const hostListenerProxy = (hostRef: d.HostRef, methodName: string) => (ev: Event) => {
39-
if (BUILD.lazyLoad) {
40-
if (hostRef.$flags$ & HOST_FLAGS.isListenReady) {
41-
// instance is ready, let's call it's member method for this event
42-
hostRef.$lazyInstance$[methodName](ev);
39+
try {
40+
if (BUILD.lazyLoad) {
41+
if (hostRef.$flags$ & HOST_FLAGS.isListenReady) {
42+
// instance is ready, let's call it's member method for this event
43+
hostRef.$lazyInstance$[methodName](ev);
44+
} else {
45+
(hostRef.$queuedListeners$ = hostRef.$queuedListeners$ || []).push([methodName, ev]);
46+
}
4347
} else {
44-
(hostRef.$queuedListeners$ = hostRef.$queuedListeners$ || []).push([methodName, ev]);
48+
(hostRef.$hostElement$ as any)[methodName](ev);
4549
}
46-
} else {
47-
(hostRef.$hostElement$ as any)[methodName](ev);
50+
} catch (e) {
51+
consoleError(e);
4852
}
4953
};
5054

src/runtime/set-value.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const setValue = (ref: d.RuntimeRef, propName: string, newVal: any, cmpMe
5757
// fire off each of the watch methods that are watching this property
5858
instance[watchMethodName](newVal, oldVal, propName);
5959
} catch (e) {
60-
consoleError(e);
60+
consoleError(e, elm);
6161
}
6262
});
6363
}

src/runtime/update-component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const updateComponent = async (hostRef: d.HostRef, instance: any, isInitialLoad:
117117
}
118118
}
119119
} catch (e) {
120-
consoleError(e);
120+
consoleError(e, elm);
121121
}
122122
}
123123

@@ -170,7 +170,7 @@ const callRender = (hostRef: d.HostRef, instance: any) => {
170170
hostRef.$flags$ |= HOST_FLAGS.hasRendered;
171171
}
172172
} catch (e) {
173-
consoleError(e);
173+
consoleError(e, hostRef.$hostElement$);
174174
}
175175
renderingRef = null;
176176
return instance;

src/runtime/vdom/vdom-render.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import type * as d from '../../declarations';
1010
import { BUILD } from '@app-data';
1111
import { CMP_FLAGS, HTML_NS, SVG_NS, isDef } from '@utils';
12-
import { consoleError, doc, plt, supportsShadow } from '@platform';
12+
import { consoleDevError, doc, plt, supportsShadow } from '@platform';
1313
import { h, isHost, newVNode } from './h';
1414
import { NODE_TYPE, PLATFORM_FLAGS, VNODE_FLAGS } from '../runtime-constants';
1515
import { updateElement } from './update-element';
@@ -52,7 +52,7 @@ const createElm = (oldParentVNode: d.VNode, newParentVNode: d.VNode, childIndex:
5252
}
5353

5454
if (BUILD.isDev && newVNode.$elm$) {
55-
consoleError(
55+
consoleDevError(
5656
`The JSX ${
5757
newVNode.$text$ !== null ? `"${newVNode.$text$}" text` : `"${newVNode.$tag$}" element`
5858
} node should not be shared within the same renderer. The renderer caches element lookups in order to improve performance. However, a side effect from this is that the exact same JSX node should not be reused. For more information please see https://stenciljs.com/docs/templating-jsx#avoid-shared-jsx-nodes`,

0 commit comments

Comments
 (0)