1
1
// DO NOT IMPORT window.config HERE!
2
- // to make sure the error handler always works, we should never import `window.config`, because some user's custom template breaks it.
2
+ // to make sure the error handler always works, we should never import `window.config`, because
3
+ // some user's custom template breaks it.
3
4
4
5
// This sets up the URL prefix used in webpack's chunk loading.
5
6
// This file must be imported before any lazy-loading is being attempted.
@@ -26,29 +27,42 @@ export function showGlobalErrorMessage(msg) {
26
27
}
27
28
28
29
/**
29
- * @param {ErrorEvent } e
30
+ * @param {ErrorEvent|PromiseRejectionEvent } event - Event
31
+ * @param {string } event.message - Only present on ErrorEvent
32
+ * @param {string } event.error - Only present on ErrorEvent
33
+ * @param {string } event.type - Only present on ErrorEvent
34
+ * @param {string } event.filename - Only present on ErrorEvent
35
+ * @param {number } event.lineno - Only present on ErrorEvent
36
+ * @param {number } event.colno - Only present on ErrorEvent
37
+ * @param {string } event.reason - Only present on PromiseRejectionEvent
38
+ * @param {number } event.promise - Only present on PromiseRejectionEvent
30
39
*/
31
- function processWindowErrorEvent ( e ) {
32
- const err = e . error ?? e . reason ;
40
+ function processWindowErrorEvent ( { error , reason , message , type , filename , lineno , colno } ) {
41
+ const err = error ?? reason ;
33
42
const assetBaseUrl = String ( new URL ( __webpack_public_path__ , window . location . origin ) ) ;
43
+ const { runModeIsProd} = window . config ?? { } ;
34
44
35
- // error is likely from browser extension or inline script. Do not show these in production builds.
36
- if ( ! err . stack ?. includes ( assetBaseUrl ) && window . config ?. runModeIsProd ) return ;
37
-
38
- let message ;
39
- if ( e . type === 'unhandledrejection' ) {
40
- message = `JavaScript promise rejection: ${ err . message } .` ;
41
- } else {
42
- message = `JavaScript error: ${ e . message } ( ${ e . filename } @ ${ e . lineno } : ${ e . colno } ).` ;
45
+ // ` error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likly a
46
+ // non-critical event from the browser. We log them but don't show them to users. Examples:
47
+ // - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors
48
+ // - https://github.com/mozilla-mobile/firefox-ios/issues/10817
49
+ // - https://github.com/go-gitea/gitea/issues/20240
50
+ if ( ! err ) {
51
+ if ( message ) console . error ( new Error ( message ) ) ;
52
+ if ( runModeIsProd ) return ;
43
53
}
44
54
45
- if ( ! e . error && e . lineno === 0 && e . colno === 0 && e . filename === '' && window . navigator . userAgent . includes ( 'FxiOS/' ) ) {
46
- // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240
47
- // If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0.
48
- return ; // ignore such nonsense error event
55
+ // If the error stack trace does not include the base URL of our script assets, it likely came
56
+ // from a browser extension or inline script. Do not show such errors in production.
57
+ if ( err instanceof Error && ! err . stack ?. includes ( assetBaseUrl ) && runModeIsProd ) {
58
+ return ;
49
59
}
50
60
51
- showGlobalErrorMessage ( `${ message } Open browser console to see more details.` ) ;
61
+ let msg = err ?. message ?? message ;
62
+ if ( lineno ) msg += ` (${ filename } @ ${ lineno } :${ colno } )` ;
63
+ const dot = msg . endsWith ( '.' ) ? '' : '.' ;
64
+ const renderedType = type === 'unhandledrejection' ? 'promise rejection' : type ;
65
+ showGlobalErrorMessage ( `JavaScript ${ renderedType } : ${ msg } ${ dot } Open browser console to see more details.` ) ;
52
66
}
53
67
54
68
function initGlobalErrorHandler ( ) {
@@ -59,13 +73,14 @@ function initGlobalErrorHandler() {
59
73
if ( ! window . config ) {
60
74
showGlobalErrorMessage ( `Gitea JavaScript code couldn't run correctly, please check your custom templates` ) ;
61
75
}
62
- // we added an event handler for window error at the very beginning of <script> of page head
63
- // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init
64
- // then in this init, we can collect all error events and show them
76
+ // we added an event handler for window error at the very beginning of <script> of page head the
77
+ // handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before
78
+ // this init then in this init, we can collect all error events and show them.
65
79
for ( const e of window . _globalHandlerErrors || [ ] ) {
66
80
processWindowErrorEvent ( e ) ;
67
81
}
68
- // then, change _globalHandlerErrors to an object with push method, to process further error events directly
82
+ // then, change _globalHandlerErrors to an object with push method, to process further error
83
+ // events directly
69
84
window . _globalHandlerErrors = { _inited : true , push : ( e ) => processWindowErrorEvent ( e ) } ;
70
85
}
71
86
0 commit comments