Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
517e197
Report DOM events to reportError directly instead of rethrowing
sebmarkbage Mar 12, 2024
bec21b2
Never rethrow at the root
sebmarkbage Mar 12, 2024
742aba4
Log fatal errors as they happen
sebmarkbage Mar 22, 2024
54471d4
Report root errors to the browser so they show up as "uncaught"
sebmarkbage Mar 12, 2024
bc5374d
Polyfill dispatching error event
sebmarkbage Mar 13, 2024
c6dd65e
Remove rethrowing in the commit phase
sebmarkbage Mar 13, 2024
0d7615d
Rethrow global errors that happened during an internal act
sebmarkbage Mar 22, 2024
ef549d4
Rethrow uncaught errors from act instead of logging them
sebmarkbage Mar 22, 2024
9f8a43a
Aggregate errors in internal act
sebmarkbage Mar 22, 2024
8fe758b
Aggregate errors in act
sebmarkbage Mar 22, 2024
254af8d
Use shared queue and only track errors once for internalAct/waitFor
sebmarkbage Mar 23, 2024
b4de7d2
Test error logging recovery without act
sebmarkbage Mar 24, 2024
06e4464
Fix tests that failed due to internalAct now rethrowing non-render er…
sebmarkbage Mar 22, 2024
785c32a
Fix tests
sebmarkbage Mar 22, 2024
e32089f
Fix tests that rely on flushSync to throw
sebmarkbage Mar 22, 2024
175484e
Use internal act for prod testing
sebmarkbage Mar 25, 2024
7344587
Build lint process for the reportGlobalError polyfill
sebmarkbage Mar 25, 2024
613ae34
Fix test
sebmarkbage Mar 27, 2024
d3f0b57
Fix legacy tests
rickhanlonii Mar 26, 2024
c06e47d
Fix legacy tests in ReactDOM-test.js
rickhanlonii Mar 26, 2024
45fb81e
Add back React.Children.only
rickhanlonii Mar 26, 2024
8e3c0ae
Fix useSyncExternalStoreShared-test.js
rickhanlonii Mar 26, 2024
7a07e98
Fix ReactFresh-test.js
rickhanlonii Mar 26, 2024
0928d91
Update error messages
sebmarkbage Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Polyfill dispatching error event
  • Loading branch information
sebmarkbage committed Mar 27, 2024
commit bc5374dd39da0e294cd90f72bc4475b5d9dc45a3
15 changes: 3 additions & 12 deletions packages/react-dom-bindings/src/events/DOMPluginEventSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ import * as SelectEventPlugin from './plugins/SelectEventPlugin';
import * as SimpleEventPlugin from './plugins/SimpleEventPlugin';
import * as FormActionEventPlugin from './plugins/FormActionEventPlugin';

import reportGlobalError from 'shared/reportGlobalError';

type DispatchListener = {
instance: null | Fiber,
listener: Function,
Expand Down Expand Up @@ -226,17 +228,6 @@ export const nonDelegatedEvents: Set<DOMEventName> = new Set([
...mediaEventTypes,
]);

const reportErrorToBrowser =
typeof reportError === 'function'
? // In modern browsers, reportError will dispatch an error event,
// emulating an uncaught JavaScript error.
reportError
: (error: mixed) => {
// In older browsers and test environments, fallback to console.error.
// eslint-disable-next-line react-internal/no-production-logging
console['error'](error);
};

function executeDispatch(
event: ReactSyntheticEvent,
listener: Function,
Expand All @@ -246,7 +237,7 @@ function executeDispatch(
try {
listener(event);
} catch (error) {
reportErrorToBrowser(error);
reportGlobalError(error);
}
event.currentTarget = null;
}
Expand Down
16 changes: 5 additions & 11 deletions packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,11 @@ import {
} from 'react-reconciler/src/ReactFiberReconciler';
import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';

/* global reportError */
const defaultOnRecoverableError =
typeof reportError === 'function'
? // In modern browsers, reportError will dispatch an error event,
// emulating an uncaught JavaScript error.
reportError
: (error: mixed) => {
// In older browsers and test environments, fallback to console.error.
// eslint-disable-next-line react-internal/no-production-logging
console['error'](error);
};
import reportGlobalError from 'shared/reportGlobalError';

function defaultOnRecoverableError(error: mixed, errorInfo: any) {
reportGlobalError(error);
}

// $FlowFixMe[missing-this-annot]
function ReactDOMRoot(internalRoot: FiberRoot) {
Expand Down
13 changes: 2 additions & 11 deletions packages/react-reconciler/src/ReactFiberErrorLogger.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,7 @@ import {showErrorDialog} from './ReactFiberErrorDialog';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
import {HostRoot} from 'react-reconciler/src/ReactWorkTags';

const reportErrorToBrowser =
typeof reportError === 'function'
? // In modern browsers, reportError will dispatch an error event,
// emulating an uncaught JavaScript error.
reportError
: (error: mixed) => {
// In older browsers and test environments, fallback to console.error.
// eslint-disable-next-line react-internal/no-production-logging
console['error'](error);
};
import reportGlobalError from 'shared/reportGlobalError';

export function logCapturedError(
boundary: Fiber,
Expand All @@ -44,7 +35,7 @@ export function logCapturedError(
// For uncaught root errors we report them as uncaught to the browser's
// onerror callback. This won't have component stacks and the error addendum.
// So we add those into a separate console.warn.
reportErrorToBrowser(error);
reportGlobalError(error);
if (__DEV__) {
const source = errorInfo.source;
const stack = errorInfo.stack;
Expand Down
19 changes: 4 additions & 15 deletions packages/react/src/ReactStartTransition.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
enableTransitionTracing,
} from 'shared/ReactFeatureFlags';

import reportGlobalError from 'shared/reportGlobalError';

export function startTransition(
scope: () => void,
options?: StartTransitionOptions,
Expand Down Expand Up @@ -51,10 +53,10 @@ export function startTransition(
typeof returnValue.then === 'function'
) {
callbacks.forEach(callback => callback(currentTransition, returnValue));
returnValue.then(noop, onError);
returnValue.then(noop, reportGlobalError);
}
} catch (error) {
onError(error);
reportGlobalError(error);
} finally {
warnAboutTransitionSubscriptions(prevTransition, currentTransition);
ReactCurrentBatchConfig.transition = prevTransition;
Expand Down Expand Up @@ -91,16 +93,3 @@ function warnAboutTransitionSubscriptions(
}

function noop() {}

// Use reportError, if it exists. Otherwise console.error. This is the same as
// the default for onRecoverableError.
const onError =
typeof reportError === 'function'
? // In modern browsers, reportError will dispatch an error event,
// emulating an uncaught JavaScript error.
reportError
: (error: mixed) => {
// In older browsers and test environments, fallback to console.error.
// eslint-disable-next-line react-internal/no-production-logging
console['error'](error);
};
52 changes: 52 additions & 0 deletions packages/shared/reportGlobalError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

const reportGlobalError: (error: mixed) => void =
typeof reportError === 'function'
? // In modern browsers, reportError will dispatch an error event,
// emulating an uncaught JavaScript error.
reportError
: error => {
if (
typeof window === 'object' &&
typeof window.ErrorEvent === 'function'
) {
// Browser Polyfill
const message =
typeof error === 'object' &&
error !== null &&
typeof error.message === 'string'
? // eslint-disable-next-line react-internal/safe-string-coercion
String(error.message)
: // eslint-disable-next-line react-internal/safe-string-coercion
String(error);
const event = new window.ErrorEvent('error', {
bubbles: true,
cancelable: true,
message: message,
error: error,
});
const shouldLog = window.dispatchEvent(event);
if (!shouldLog) {
return;
}
} else if (
typeof process === 'object' &&
// $FlowFixMe[method-unbinding]
typeof process.emit === 'function'
) {
// Node Polyfill
process.emit('uncaughtException', error);
return;
}
// eslint-disable-next-line react-internal/no-production-logging
console['error'](error);
};

export default reportGlobalError;