From 8997dd0d28100be74bbab7ff8edf69ca5ba2db1a Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Mon, 25 Aug 2025 22:08:36 +0200 Subject: [PATCH 1/5] [LogBox] Parse bundling errors from Metro Dev Server and avoid symbolicating them --- .../Libraries/LogBox/Data/parseLogBoxLog.js | 121 ++++++++++-------- .../react-native/Libraries/LogBox/LogBox.js | 15 +++ 2 files changed, 82 insertions(+), 54 deletions(-) diff --git a/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js b/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js index 89b1626b8e7b..48c3b7295cbc 100644 --- a/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js +++ b/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js @@ -290,7 +290,68 @@ export function parseLogBoxException( const message = error.originalMessage != null ? error.originalMessage : 'Unknown'; - const metroInternalError = message.match(RE_METRO_ERROR_FORMAT); + const bundlingError = parseBundlingErrorFromMessage(message); + if (bundlingError) { + return bundlingError; + } + + const componentStack = error.componentStack; + if (error.isFatal || error.isComponentError) { + if (componentStack != null) { + const {type, stack} = parseComponentStack(componentStack); + return { + level: 'fatal', + stack: error.stack, + isComponentError: error.isComponentError, + componentStackType: type, + componentStack: stack, + extraData: error.extraData, + ...parseInterpolation([message]), + }; + } else { + return { + level: 'fatal', + stack: error.stack, + isComponentError: error.isComponentError, + componentStackType: 'legacy', + componentStack: [], + extraData: error.extraData, + ...parseInterpolation([message]), + }; + } + } + + if (componentStack != null) { + // It is possible that console errors have a componentStack. + const {type, stack} = parseComponentStack(componentStack); + return { + level: 'error', + stack: error.stack, + isComponentError: error.isComponentError, + componentStackType: type, + componentStack: stack, + extraData: error.extraData, + ...parseInterpolation([message]), + }; + } + + // Most `console.error` calls won't have a componentStack. We parse them like + // regular logs which have the component stack buried in the message. + return { + level: 'error', + stack: error.stack, + isComponentError: error.isComponentError, + extraData: error.extraData, + ...parseLogBoxLog([message]), + }; +} + +export function parseBundlingErrorFromMessage(message?: string, extraData?: Object): LogBoxLogData | null { + if (!message || typeof message !== 'string') { + return null; + } + + const metroInternalError = message.match(RE_METRO_ERROR_FORMAT); if (metroInternalError) { const [content, fileName, row, column, codeFrame] = metroInternalError.slice(1); @@ -315,7 +376,7 @@ export function parseLogBoxException( substitutions: [], }, category: `${fileName}-${row}-${column}`, - extraData: error.extraData, + extraData, }; } @@ -344,7 +405,7 @@ export function parseLogBoxException( substitutions: [], }, category: `${fileName}-${row}-${column}`, - extraData: error.extraData, + extraData, }; } @@ -372,7 +433,7 @@ export function parseLogBoxException( substitutions: [], }, category: `${fileName}-${1}-${1}`, - extraData: error.extraData, + extraData, }; } } @@ -389,59 +450,11 @@ export function parseLogBoxException( substitutions: [], }, category: message, - extraData: error.extraData, - }; - } - - const componentStack = error.componentStack; - if (error.isFatal || error.isComponentError) { - if (componentStack != null) { - const {type, stack} = parseComponentStack(componentStack); - return { - level: 'fatal', - stack: error.stack, - isComponentError: error.isComponentError, - componentStackType: type, - componentStack: stack, - extraData: error.extraData, - ...parseInterpolation([message]), - }; - } else { - return { - level: 'fatal', - stack: error.stack, - isComponentError: error.isComponentError, - componentStackType: 'legacy', - componentStack: [], - extraData: error.extraData, - ...parseInterpolation([message]), - }; - } - } - - if (componentStack != null) { - // It is possible that console errors have a componentStack. - const {type, stack} = parseComponentStack(componentStack); - return { - level: 'error', - stack: error.stack, - isComponentError: error.isComponentError, - componentStackType: type, - componentStack: stack, - extraData: error.extraData, - ...parseInterpolation([message]), + extraData, }; } - // Most `console.error` calls won't have a componentStack. We parse them like - // regular logs which have the component stack buried in the message. - return { - level: 'error', - stack: error.stack, - isComponentError: error.isComponentError, - extraData: error.extraData, - ...parseLogBoxLog([message]), - }; + return null; } export function withoutANSIColorStyles(message: mixed): mixed { diff --git a/packages/react-native/Libraries/LogBox/LogBox.js b/packages/react-native/Libraries/LogBox/LogBox.js index a9bfdd760edc..962343c01b29 100644 --- a/packages/react-native/Libraries/LogBox/LogBox.js +++ b/packages/react-native/Libraries/LogBox/LogBox.js @@ -39,6 +39,7 @@ if (__DEV__) { const { parseLogBoxLog, parseComponentStack, + parseBundlingErrorFromMessage, } = require('./Data/parseLogBoxLog'); let originalConsoleWarn; @@ -166,6 +167,20 @@ if (__DEV__) { } } + // Ensure bundling errors are parsed before anything else. + // This ensures bundling errors are not symbolicated + // (symbolication would fail since the bundle & source map won't be created) + const bundlingError = parseBundlingErrorFromMessage( + // If args[0] is a string use it directly, otherwise try to extract message property + typeof args[0] === 'string' + ? args[0] + : typeof args[0]?.message === 'string' + ? args[0].message + : undefined) + if (bundlingError) { + return LogBoxData.addLog(bundlingError); + } + const result = parseLogBoxLog(args); const category = result.category; const message = result.message; From 47cc6701b112f27c70df9bd3ddca71e4e7cc98d0 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Tue, 26 Aug 2025 11:22:15 +0200 Subject: [PATCH 2/5] Revert "[LogBox] Parse bundling errors from Metro Dev Server and avoid symbolicating them" This reverts commit 8997dd0d28100be74bbab7ff8edf69ca5ba2db1a. --- .../Libraries/LogBox/Data/parseLogBoxLog.js | 121 ++++++++---------- .../react-native/Libraries/LogBox/LogBox.js | 15 --- 2 files changed, 54 insertions(+), 82 deletions(-) diff --git a/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js b/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js index 48c3b7295cbc..89b1626b8e7b 100644 --- a/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js +++ b/packages/react-native/Libraries/LogBox/Data/parseLogBoxLog.js @@ -290,68 +290,7 @@ export function parseLogBoxException( const message = error.originalMessage != null ? error.originalMessage : 'Unknown'; - const bundlingError = parseBundlingErrorFromMessage(message); - if (bundlingError) { - return bundlingError; - } - - const componentStack = error.componentStack; - if (error.isFatal || error.isComponentError) { - if (componentStack != null) { - const {type, stack} = parseComponentStack(componentStack); - return { - level: 'fatal', - stack: error.stack, - isComponentError: error.isComponentError, - componentStackType: type, - componentStack: stack, - extraData: error.extraData, - ...parseInterpolation([message]), - }; - } else { - return { - level: 'fatal', - stack: error.stack, - isComponentError: error.isComponentError, - componentStackType: 'legacy', - componentStack: [], - extraData: error.extraData, - ...parseInterpolation([message]), - }; - } - } - - if (componentStack != null) { - // It is possible that console errors have a componentStack. - const {type, stack} = parseComponentStack(componentStack); - return { - level: 'error', - stack: error.stack, - isComponentError: error.isComponentError, - componentStackType: type, - componentStack: stack, - extraData: error.extraData, - ...parseInterpolation([message]), - }; - } - - // Most `console.error` calls won't have a componentStack. We parse them like - // regular logs which have the component stack buried in the message. - return { - level: 'error', - stack: error.stack, - isComponentError: error.isComponentError, - extraData: error.extraData, - ...parseLogBoxLog([message]), - }; -} - -export function parseBundlingErrorFromMessage(message?: string, extraData?: Object): LogBoxLogData | null { - if (!message || typeof message !== 'string') { - return null; - } - - const metroInternalError = message.match(RE_METRO_ERROR_FORMAT); + const metroInternalError = message.match(RE_METRO_ERROR_FORMAT); if (metroInternalError) { const [content, fileName, row, column, codeFrame] = metroInternalError.slice(1); @@ -376,7 +315,7 @@ export function parseBundlingErrorFromMessage(message?: string, extraData?: Obje substitutions: [], }, category: `${fileName}-${row}-${column}`, - extraData, + extraData: error.extraData, }; } @@ -405,7 +344,7 @@ export function parseBundlingErrorFromMessage(message?: string, extraData?: Obje substitutions: [], }, category: `${fileName}-${row}-${column}`, - extraData, + extraData: error.extraData, }; } @@ -433,7 +372,7 @@ export function parseBundlingErrorFromMessage(message?: string, extraData?: Obje substitutions: [], }, category: `${fileName}-${1}-${1}`, - extraData, + extraData: error.extraData, }; } } @@ -450,11 +389,59 @@ export function parseBundlingErrorFromMessage(message?: string, extraData?: Obje substitutions: [], }, category: message, - extraData, + extraData: error.extraData, + }; + } + + const componentStack = error.componentStack; + if (error.isFatal || error.isComponentError) { + if (componentStack != null) { + const {type, stack} = parseComponentStack(componentStack); + return { + level: 'fatal', + stack: error.stack, + isComponentError: error.isComponentError, + componentStackType: type, + componentStack: stack, + extraData: error.extraData, + ...parseInterpolation([message]), + }; + } else { + return { + level: 'fatal', + stack: error.stack, + isComponentError: error.isComponentError, + componentStackType: 'legacy', + componentStack: [], + extraData: error.extraData, + ...parseInterpolation([message]), + }; + } + } + + if (componentStack != null) { + // It is possible that console errors have a componentStack. + const {type, stack} = parseComponentStack(componentStack); + return { + level: 'error', + stack: error.stack, + isComponentError: error.isComponentError, + componentStackType: type, + componentStack: stack, + extraData: error.extraData, + ...parseInterpolation([message]), }; } - return null; + // Most `console.error` calls won't have a componentStack. We parse them like + // regular logs which have the component stack buried in the message. + return { + level: 'error', + stack: error.stack, + isComponentError: error.isComponentError, + extraData: error.extraData, + ...parseLogBoxLog([message]), + }; } export function withoutANSIColorStyles(message: mixed): mixed { diff --git a/packages/react-native/Libraries/LogBox/LogBox.js b/packages/react-native/Libraries/LogBox/LogBox.js index 962343c01b29..a9bfdd760edc 100644 --- a/packages/react-native/Libraries/LogBox/LogBox.js +++ b/packages/react-native/Libraries/LogBox/LogBox.js @@ -39,7 +39,6 @@ if (__DEV__) { const { parseLogBoxLog, parseComponentStack, - parseBundlingErrorFromMessage, } = require('./Data/parseLogBoxLog'); let originalConsoleWarn; @@ -167,20 +166,6 @@ if (__DEV__) { } } - // Ensure bundling errors are parsed before anything else. - // This ensures bundling errors are not symbolicated - // (symbolication would fail since the bundle & source map won't be created) - const bundlingError = parseBundlingErrorFromMessage( - // If args[0] is a string use it directly, otherwise try to extract message property - typeof args[0] === 'string' - ? args[0] - : typeof args[0]?.message === 'string' - ? args[0].message - : undefined) - if (bundlingError) { - return LogBoxData.addLog(bundlingError); - } - const result = parseLogBoxLog(args); const category = result.category; const message = result.message; From 7db31e2fca0f828aa6bf489ae6dc4adef9b7b7c3 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Tue, 26 Aug 2025 11:23:12 +0200 Subject: [PATCH 3/5] fix the issue in hmrclient --- packages/react-native/Libraries/Utilities/HMRClient.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/Utilities/HMRClient.js b/packages/react-native/Libraries/Utilities/HMRClient.js index 6d16b6b973f3..fecb3da26566 100644 --- a/packages/react-native/Libraries/Utilities/HMRClient.js +++ b/packages/react-native/Libraries/Utilities/HMRClient.js @@ -367,7 +367,8 @@ function showCompileError() { // Symbolicating compile errors is wasted effort // because the stack trace is meaningless: error.preventSymbolication = true; - throw error; + error.originalMessage = message; + LogBox.addException(error); } export default HMRClient; From 1dfa91ab411b67811a32005260f269cc15aab0ba Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Thu, 16 Oct 2025 20:57:32 +0200 Subject: [PATCH 4/5] fix flow type checking issues --- .../Libraries/Utilities/HMRClient.js | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/react-native/Libraries/Utilities/HMRClient.js b/packages/react-native/Libraries/Utilities/HMRClient.js index fecb3da26566..33ed6e4fc66b 100644 --- a/packages/react-native/Libraries/Utilities/HMRClient.js +++ b/packages/react-native/Libraries/Utilities/HMRClient.js @@ -349,7 +349,8 @@ function dismissRedbox() { } function showCompileError() { - if (currentCompileErrorMessage === null) { + const message = currentCompileErrorMessage; + if (message === null) { return; } @@ -357,18 +358,18 @@ function showCompileError() { // Otherwise you risk seeing a stale runtime error while a syntax error is more recent. dismissRedbox(); - const message = currentCompileErrorMessage; currentCompileErrorMessage = null; - /* $FlowFixMe[class-object-subtyping] added when improving typing for this - * parameters */ - // $FlowFixMe[incompatible-type] - const error: ExtendedError = new Error(message); - // Symbolicating compile errors is wasted effort - // because the stack trace is meaningless: - error.preventSymbolication = true; - error.originalMessage = message; - LogBox.addException(error); + LogBox.addException({ + message: message, + originalMessage: message, + name: undefined, + componentStack: undefined, + stack: [], + id: -1, + isFatal: true, + isComponentError: false, + }); } export default HMRClient; From 93ac7db54ead26da002abebb8f987f58b6be3a1d Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Thu, 16 Oct 2025 21:00:06 +0200 Subject: [PATCH 5/5] fix eslint warnings --- packages/react-native/Libraries/Utilities/HMRClient.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-native/Libraries/Utilities/HMRClient.js b/packages/react-native/Libraries/Utilities/HMRClient.js index 33ed6e4fc66b..0d112fcb489a 100644 --- a/packages/react-native/Libraries/Utilities/HMRClient.js +++ b/packages/react-native/Libraries/Utilities/HMRClient.js @@ -8,8 +8,6 @@ * @format */ -import type {ExtendedError} from '../Core/ExtendedError'; - import getDevServer from '../Core/Devtools/getDevServer'; import LogBox from '../LogBox/LogBox'; import NativeRedBox from '../NativeModules/specs/NativeRedBox';