diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index c5476eaab..b5868139c 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -16,6 +16,7 @@ var RCTExceptionsManager = require('NativeModules').ExceptionsManager; var loadSourceMap = require('loadSourceMap'); var parseErrorStack = require('parseErrorStack'); +var stringifySafe = require('stringifySafe'); var sourceMapPromise; @@ -25,17 +26,11 @@ type Exception = { message: string; } -function handleException(e: Exception) { - var stack = parseErrorStack(e); - console.error( - 'Err0r: ' + - '\n stack: \n' + stackToString(stack) + - '\n URL: ' + e.sourceURL + - '\n line: ' + e.line + - '\n message: ' + e.message - ); - +function reportException(e: Exception, stack?: any) { if (RCTExceptionsManager) { + if (!stack) { + stack = parseErrorStack(e); + } RCTExceptionsManager.reportUnhandledException(e.message, stack); if (__DEV__) { (sourceMapPromise = sourceMapPromise || loadSourceMap()) @@ -50,6 +45,47 @@ function handleException(e: Exception) { } } +function handleException(e: Exception) { + var stack = parseErrorStack(e); + var msg = + 'Error: ' + e.message + + '\n stack: \n' + stackToString(stack) + + '\n URL: ' + e.sourceURL + + '\n line: ' + e.line + + '\n message: ' + e.message; + if (console.errorOriginal) { + console.errorOriginal(msg); + } else { + console.error(msg); + } + reportException(e, stack); +} + +/** + * Shows a redbox with stacktrace for all console.error messages. Disable by + * setting `console.reportErrorsAsExceptions = false;` in your app. + */ +function installConsoleErrorReporter() { + if (console.reportException) { + return; // already installed + } + console.reportException = reportException; + console.errorOriginal = console.error.bind(console); + console.error = function reactConsoleError() { + console.errorOriginal.apply(null, arguments); + if (!console.reportErrorsAsExceptions) { + return; + } + var str = Array.prototype.map.call(arguments, stringifySafe).join(', '); + var error: any = new Error('console.error: ' + str); + error.framesToPop = 1; + reportException(error); + }; + if (console.reportErrorsAsExceptions === undefined) { + console.reportErrorsAsExceptions = true; // Individual apps can disable this + } +} + function stackToString(stack) { var maxLength = Math.max.apply(null, stack.map(frame => frame.methodName.length)); return stack.map(frame => stackFrameToString(frame, maxLength)).join('\n'); @@ -71,4 +107,4 @@ function fillSpaces(n) { return new Array(n + 1).join(' '); } -module.exports = { handleException }; +module.exports = { handleException, installConsoleErrorReporter }; diff --git a/Libraries/ReactIOS/renderApplication.ios.js b/Libraries/ReactIOS/renderApplication.ios.js index 16052c6fa..39e5720f5 100644 --- a/Libraries/ReactIOS/renderApplication.ios.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -11,6 +11,8 @@ */ 'use strict'; +require('ExceptionsManager').installConsoleErrorReporter(); + var React = require('React'); var StyleSheet = require('StyleSheet'); var View = require('View');