123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- Object.defineProperty(exports, '__esModule', { value: true });
- const browser = require('@sentry/browser');
- const utils = require('@sentry/utils');
- const hoistNonReactStatics = require('hoist-non-react-statics');
- const React = require('react');
- const debugBuild = require('./debug-build.js');
- const _interopDefault = e => e && e.__esModule ? e.default : e;
- function _interopNamespace(e) {
- if (e && e.__esModule) return e;
- const n = Object.create(null);
- if (e) {
- for (const k in e) {
- n[k] = e[k];
- }
- }
- n.default = e;
- return n;
- }
- const hoistNonReactStatics__default = /*#__PURE__*/_interopDefault(hoistNonReactStatics);
- const React__namespace = /*#__PURE__*/_interopNamespace(React);
- const _jsxFileName = "/home/runner/work/sentry-javascript/sentry-javascript/packages/react/src/errorboundary.tsx";
- function isAtLeastReact17(version) {
- const major = version.match(/^([^.]+)/);
- return major !== null && parseInt(major[0]) >= 17;
- }
- const UNKNOWN_COMPONENT = 'unknown';
- const INITIAL_STATE = {
- componentStack: null,
- error: null,
- eventId: null,
- };
- function setCause(error, cause) {
- const seenErrors = new WeakMap();
- function recurse(error, cause) {
- // If we've already seen the error, there is a recursive loop somewhere in the error's
- // cause chain. Let's just bail out then to prevent a stack overflow.
- if (seenErrors.has(error)) {
- return;
- }
- if (error.cause) {
- seenErrors.set(error, true);
- return recurse(error.cause, cause);
- }
- error.cause = cause;
- }
- recurse(error, cause);
- }
- /**
- * A ErrorBoundary component that logs errors to Sentry. Requires React >= 16.
- * NOTE: If you are a Sentry user, and you are seeing this stack frame, it means the
- * Sentry React SDK ErrorBoundary caught an error invoking your application code. This
- * is expected behavior and NOT indicative of a bug with the Sentry React SDK.
- */
- class ErrorBoundary extends React__namespace.Component {
- constructor(props) {
- super(props);ErrorBoundary.prototype.__init.call(this);
- this.state = INITIAL_STATE;
- this._openFallbackReportDialog = true;
- const client = browser.getClient();
- if (client && client.on && props.showDialog) {
- this._openFallbackReportDialog = false;
- client.on('afterSendEvent', event => {
- if (!event.type && event.event_id === this._lastEventId) {
- // eslint-disable-next-line deprecation/deprecation
- browser.showReportDialog({ ...props.dialogOptions, eventId: this._lastEventId });
- }
- });
- }
- }
- componentDidCatch(error, { componentStack }) {
- const { beforeCapture, onError, showDialog, dialogOptions } = this.props;
- browser.withScope(scope => {
- // If on React version >= 17, create stack trace from componentStack param and links
- // to to the original error using `error.cause` otherwise relies on error param for stacktrace.
- // Linking errors requires the `LinkedErrors` integration be enabled.
- // See: https://reactjs.org/blog/2020/08/10/react-v17-rc.html#native-component-stacks
- //
- // Although `componentDidCatch` is typed to accept an `Error` object, it can also be invoked
- // with non-error objects. This is why we need to check if the error is an error-like object.
- // See: https://github.com/getsentry/sentry-javascript/issues/6167
- if (isAtLeastReact17(React__namespace.version) && utils.isError(error)) {
- const errorBoundaryError = new Error(error.message);
- errorBoundaryError.name = `React ErrorBoundary ${error.name}`;
- errorBoundaryError.stack = componentStack;
- // Using the `LinkedErrors` integration to link the errors together.
- setCause(error, errorBoundaryError);
- }
- if (beforeCapture) {
- beforeCapture(scope, error, componentStack);
- }
- const eventId = browser.captureException(error, {
- captureContext: {
- contexts: { react: { componentStack } },
- },
- mechanism: { handled: false },
- });
- if (onError) {
- onError(error, componentStack, eventId);
- }
- if (showDialog) {
- this._lastEventId = eventId;
- if (this._openFallbackReportDialog) {
- browser.showReportDialog({ ...dialogOptions, eventId });
- }
- }
- // componentDidCatch is used over getDerivedStateFromError
- // so that componentStack is accessible through state.
- this.setState({ error, componentStack, eventId });
- });
- }
- componentDidMount() {
- const { onMount } = this.props;
- if (onMount) {
- onMount();
- }
- }
- componentWillUnmount() {
- const { error, componentStack, eventId } = this.state;
- const { onUnmount } = this.props;
- if (onUnmount) {
- onUnmount(error, componentStack, eventId);
- }
- }
- __init() {this.resetErrorBoundary = () => {
- const { onReset } = this.props;
- const { error, componentStack, eventId } = this.state;
- if (onReset) {
- onReset(error, componentStack, eventId);
- }
- this.setState(INITIAL_STATE);
- };}
- render() {
- const { fallback, children } = this.props;
- const state = this.state;
- if (state.error) {
- let element = undefined;
- if (typeof fallback === 'function') {
- element = fallback({
- error: state.error,
- componentStack: state.componentStack,
- resetError: this.resetErrorBoundary,
- eventId: state.eventId,
- });
- } else {
- element = fallback;
- }
- if (React__namespace.isValidElement(element)) {
- return element;
- }
- if (fallback) {
- debugBuild.DEBUG_BUILD && utils.logger.warn('fallback did not produce a valid ReactElement');
- }
- // Fail gracefully if no fallback provided or is not valid
- return null;
- }
- if (typeof children === 'function') {
- return (children )();
- }
- return children;
- }
- }
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- function withErrorBoundary(
- WrappedComponent,
- errorBoundaryOptions,
- ) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- const componentDisplayName = WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
- const Wrapped = (props) => (
- React__namespace.createElement(ErrorBoundary, { ...errorBoundaryOptions, __self: this, __source: {fileName: _jsxFileName, lineNumber: 238}}
- , React__namespace.createElement(WrappedComponent, { ...props, __self: this, __source: {fileName: _jsxFileName, lineNumber: 239}} )
- )
- );
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- Wrapped.displayName = `errorBoundary(${componentDisplayName})`;
- // Copy over static methods from Wrapped component to Profiler HOC
- // See: https://reactjs.org/docs/higher-order-components.html#static-methods-must-be-copied-over
- hoistNonReactStatics__default(Wrapped, WrappedComponent);
- return Wrapped;
- }
- exports.ErrorBoundary = ErrorBoundary;
- exports.UNKNOWN_COMPONENT = UNKNOWN_COMPONENT;
- exports.isAtLeastReact17 = isAtLeastReact17;
- exports.withErrorBoundary = withErrorBoundary;
- //# sourceMappingURL=errorboundary.js.map
|