react/packages/internal-test-utils/shouldIgnoreConsoleError.js
Sebastian Markbåge 349a99a7a3
Badge Environment Name on Thrown Errors from the Server (#29846)
When we replay logs we badge them with e.g. `[Server]`. That way it's
easy to identify that the source of the log actually happened on the
Server (RSC). However, when we threw an error we didn't have any such
thing. The error was rethrown on the client and then handled just like
any other client error.

This transfers the `environmentName` in DEV to our restored Error
"sub-class" (conceptually) along with `digest`. That way you can read
`error.environmentName` to print this in your own UI.

I also updated our default for `onCaughtError` (and `onError` in Fizz)
to use the `printToConsole` helper that the Flight Client uses to log it
with the badge format. So by default you get the same experience as
console.error for caught errors:

<img width="810" alt="Screenshot 2024-06-10 at 9 25 12 PM"
src="https://github.com/facebook/react/assets/63648/8490fedc-09f6-4286-9332-fbe6b0faa2d3">

<img width="815" alt="Screenshot 2024-06-10 at 9 39 30 PM"
src="https://github.com/facebook/react/assets/63648/bdcfc554-504a-4b1d-82bf-b717e74975ac">

Unfortunately I can't do the same thing for `onUncaughtError` nor
`onRecoverableError` because they use `reportError` which doesn't have
custom formatting (unless we also prevented default on window.onerror).
However maybe that's ok because 1) you should always have an error
boundary 2) it's not likely that an RSC error can actually recover
because it's not going to be rendered again so shouldn't really happen
outside some parent conditionally rendering maybe.

The other problem with this approach is that the default is no longer
trivial - so reimplementing the default in user space is trickier and
ideally we shouldn't expose our default to be called.
2024-06-26 13:27:26 -04:00

55 lines
1.8 KiB
JavaScript

'use strict';
module.exports = function shouldIgnoreConsoleError(format, args) {
if (__DEV__) {
if (typeof format === 'string') {
if (format.startsWith('%c%s')) {
// Looks like a badged error message
args.splice(0, 3);
}
if (
args[0] != null &&
((typeof args[0] === 'object' &&
typeof args[0].message === 'string' &&
// This specific log has the same signature as error logging.
// The trick is to get rid of this whole file.
!format.includes('Failed to serialize an action') &&
typeof args[0].stack === 'string') ||
(typeof args[0] === 'string' &&
args[0].indexOf('An error occurred in ') === 0))
) {
// This looks like an error with addendum from ReactFiberErrorLogger.
// They are noisy too so we'll try to ignore them.
return true;
}
if (
format.indexOf('ReactDOM.render was removed in React 19') !== -1 ||
format.indexOf('ReactDOM.hydrate was removed in React 19') !== -1 ||
format.indexOf(
'ReactDOM.render has not been supported since React 18',
) !== -1 ||
format.indexOf(
'ReactDOM.hydrate has not been supported since React 18',
) !== -1 ||
format.indexOf('react-test-renderer is deprecated.') !== -1
) {
// We haven't finished migrating our tests to use createRoot.
return true;
}
}
} else {
if (
format != null &&
typeof format.message === 'string' &&
typeof format.stack === 'string' &&
args.length === 0
) {
// In production, ReactFiberErrorLogger logs error objects directly.
// They are noisy too so we'll try to ignore them.
return true;
}
}
// Looks legit
return false;
};