diff --git a/Libraries/Core/ExceptionsManager.js b/Libraries/Core/ExceptionsManager.js index 08fe06f72..dfc41cd15 100644 --- a/Libraries/Core/ExceptionsManager.js +++ b/Libraries/Core/ExceptionsManager.js @@ -12,6 +12,13 @@ import type {ExtendedError} from 'parseErrorStack'; +const INTERNAL_CALLSITES_REGEX = new RegExp( + [ + '/Libraries/Renderer/oss/ReactNativeRenderer-dev\\.js$', + '/Libraries/BatchedBridge/MessageQueue\\.js$', + ].join('|'), +); + /** * Handles the developer-visible aspect of errors and exceptions */ @@ -38,9 +45,12 @@ function reportException(e: ExtendedError, isFatal: boolean) { symbolicateStackTrace(stack) .then(prettyStack => { if (prettyStack) { + const stackWithoutInternalCallsites = prettyStack.filter( + frame => frame.file.match(INTERNAL_CALLSITES_REGEX) === null, + ); ExceptionsManager.updateExceptionMessage( e.message, - prettyStack, + stackWithoutInternalCallsites, currentExceptionID, ); } else { diff --git a/React/Modules/RCTRedBox.m b/React/Modules/RCTRedBox.m index d7c13065e..7ed4900d3 100644 --- a/React/Modules/RCTRedBox.m +++ b/React/Modules/RCTRedBox.m @@ -134,7 +134,7 @@ CGFloat buttonWidth = self.bounds.size.width / 4; CGFloat bottomButtonHeight = self.bounds.size.height - buttonHeight - [self bottomSafeViewHeight]; - + dismissButton.frame = CGRectMake(0, bottomButtonHeight, buttonWidth, buttonHeight); reloadButton.frame = CGRectMake(buttonWidth, bottomButtonHeight, buttonWidth, buttonHeight); copyButton.frame = CGRectMake(buttonWidth * 2, bottomButtonHeight, buttonWidth, buttonHeight); @@ -148,11 +148,11 @@ [rootView addSubview:copyButton]; [rootView addSubview:extraButton]; [rootView addSubview:topBorder]; - + UIView *bottomSafeView = [UIView new]; bottomSafeView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1]; bottomSafeView.frame = CGRectMake(0, self.bounds.size.height - [self bottomSafeViewHeight], self.bounds.size.width, [self bottomSafeViewHeight]); - + [rootView addSubview:bottomSafeView]; } return self; @@ -176,14 +176,24 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (NSString *)stripAnsi:(NSString *)text +{ + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\x1b\\[[0-9;]*m" options:NSRegularExpressionCaseInsensitive error:&error]; + return [regex stringByReplacingMatchesInString:text options:0 range:NSMakeRange(0, [text length]) withTemplate:@""]; +} + - (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack isUpdate:(BOOL)isUpdate { + // Remove ANSI color codes from the message + NSString *messageWithoutAnsi = [self stripAnsi:message]; + // Show if this is a new message, or if we're updating the previous message - if ((self.hidden && !isUpdate) || (!self.hidden && isUpdate && [_lastErrorMessage isEqualToString:message])) { + if ((self.hidden && !isUpdate) || (!self.hidden && isUpdate && [_lastErrorMessage isEqualToString:messageWithoutAnsi])) { _lastStackTrace = stack; // message is displayed using UILabel, which is unable to render text of // unlimited length, so we truncate it - _lastErrorMessage = [message substringToIndex:MIN((NSUInteger)10000, message.length)]; + _lastErrorMessage = [messageWithoutAnsi substringToIndex:MIN((NSUInteger)10000, messageWithoutAnsi.length)]; [_stackTraceTableView reloadData]; diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java index 361d27d3c..14c0867bb 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java @@ -169,7 +169,8 @@ import org.json.JSONObject; ? (TextView) convertView : (TextView) LayoutInflater.from(parent.getContext()) .inflate(R.layout.redbox_item_title, parent, false); - title.setText(mTitle); + // Remove ANSI color codes from the title + title.setText(mTitle.replaceAll("\\x1b\\[[0-9;]*m", "")); return title; } else { if (convertView == null) {