code.gitea.io/gitea@v1.22.3/web_src/js/bootstrap.js (about) 1 // DO NOT IMPORT window.config HERE! 2 // to make sure the error handler always works, we should never import `window.config`, because 3 // some user's custom template breaks it. 4 5 // This sets up the URL prefix used in webpack's chunk loading. 6 // This file must be imported before any lazy-loading is being attempted. 7 __webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`; 8 9 function shouldIgnoreError(err) { 10 const ignorePatterns = [ 11 '/assets/js/monaco.', // https://github.com/go-gitea/gitea/issues/30861 , https://github.com/microsoft/monaco-editor/issues/4496 12 ]; 13 for (const pattern of ignorePatterns) { 14 if (err.stack?.includes(pattern)) return true; 15 } 16 return false; 17 } 18 19 export function showGlobalErrorMessage(msg, msgType = 'error') { 20 const msgContainer = document.querySelector('.page-content') ?? document.body; 21 const msgCompact = msg.replace(/\W/g, '').trim(); // compact the message to a data attribute to avoid too many duplicated messages 22 let msgDiv = msgContainer.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`); 23 if (!msgDiv) { 24 const el = document.createElement('div'); 25 el.innerHTML = `<div class="ui container js-global-error tw-my-[--page-spacing]"><div class="ui ${msgType} message tw-text-center tw-whitespace-pre-line"></div></div>`; 26 msgDiv = el.childNodes[0]; 27 } 28 // merge duplicated messages into "the message (count)" format 29 const msgCount = Number(msgDiv.getAttribute(`data-global-error-msg-count`)) + 1; 30 msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact); 31 msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString()); 32 msgDiv.querySelector('.ui.message').textContent = msg + (msgCount > 1 ? ` (${msgCount})` : ''); 33 msgContainer.prepend(msgDiv); 34 } 35 36 /** 37 * @param {ErrorEvent|PromiseRejectionEvent} event - Event 38 * @param {string} event.message - Only present on ErrorEvent 39 * @param {string} event.error - Only present on ErrorEvent 40 * @param {string} event.type - Only present on ErrorEvent 41 * @param {string} event.filename - Only present on ErrorEvent 42 * @param {number} event.lineno - Only present on ErrorEvent 43 * @param {number} event.colno - Only present on ErrorEvent 44 * @param {string} event.reason - Only present on PromiseRejectionEvent 45 * @param {number} event.promise - Only present on PromiseRejectionEvent 46 */ 47 function processWindowErrorEvent({error, reason, message, type, filename, lineno, colno}) { 48 const err = error ?? reason; 49 const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin)); 50 const {runModeIsProd} = window.config ?? {}; 51 52 // `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likely a 53 // non-critical event from the browser. We log them but don't show them to users. Examples: 54 // - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors 55 // - https://github.com/mozilla-mobile/firefox-ios/issues/10817 56 // - https://github.com/go-gitea/gitea/issues/20240 57 if (!err) { 58 if (message) console.error(new Error(message)); 59 if (runModeIsProd) return; 60 } 61 62 if (err instanceof Error) { 63 // If the error stack trace does not include the base URL of our script assets, it likely came 64 // from a browser extension or inline script. Do not show such errors in production. 65 if (!err.stack?.includes(assetBaseUrl) && runModeIsProd) return; 66 // Ignore some known errors that are unable to fix 67 if (shouldIgnoreError(err)) return; 68 } 69 70 let msg = err?.message ?? message; 71 if (lineno) msg += ` (${filename} @ ${lineno}:${colno})`; 72 const dot = msg.endsWith('.') ? '' : '.'; 73 const renderedType = type === 'unhandledrejection' ? 'promise rejection' : type; 74 showGlobalErrorMessage(`JavaScript ${renderedType}: ${msg}${dot} Open browser console to see more details.`); 75 } 76 77 function initGlobalErrorHandler() { 78 if (window._globalHandlerErrors?._inited) { 79 showGlobalErrorMessage(`The global error handler has been initialized, do not initialize it again`); 80 return; 81 } 82 if (!window.config) { 83 showGlobalErrorMessage(`Gitea JavaScript code couldn't run correctly, please check your custom templates`); 84 } 85 // we added an event handler for window error at the very beginning of <script> of page head the 86 // handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before 87 // this init then in this init, we can collect all error events and show them. 88 for (const e of window._globalHandlerErrors || []) { 89 processWindowErrorEvent(e); 90 } 91 // then, change _globalHandlerErrors to an object with push method, to process further error 92 // events directly 93 window._globalHandlerErrors = {_inited: true, push: (e) => processWindowErrorEvent(e)}; 94 } 95 96 initGlobalErrorHandler();