/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule ExceptionsManager * @flow */ 'use strict'; let exceptionID = 0; /** * Handles the developer-visible aspect of errors and exceptions */ function reportException(e: Error, isFatal: bool) { const parseErrorStack = require('parseErrorStack'); const symbolicateStackTrace = require('symbolicateStackTrace'); const RCTExceptionsManager = require('NativeModules').ExceptionsManager; const currentExceptionID = ++exceptionID; if (RCTExceptionsManager) { const stack = parseErrorStack(e); if (isFatal) { RCTExceptionsManager.reportFatalException(e.message, stack, currentExceptionID); } else { RCTExceptionsManager.reportSoftException(e.message, stack, currentExceptionID); } if (__DEV__) { symbolicateStackTrace(stack).then( (prettyStack) => { if (prettyStack) { RCTExceptionsManager.updateExceptionMessage(e.message, prettyStack, currentExceptionID); } else { throw new Error('The stack is null'); } } ).catch( (error) => console.warn('Unable to symbolicate stack trace: ' + error.message) ); } } } /** * Logs exceptions to the (native) console and displays them */ function handleException(e: Error, isFatal: boolean) { // Workaround for reporting errors caused by `throw 'some string'` // Unfortunately there is no way to figure out the stacktrace in this // case, so if you ended up here trying to trace an error, look for // `throw ''` somewhere in your codebase. if (!e.message) { e = new Error(e); } (console._errorOriginal || console.error)(e.message); reportException(e, isFatal); } /** * Shows a redbox with stacktrace for all console.error messages. Disable by * setting `console.reportErrorsAsExceptions = false;` in your app. */ function installConsoleErrorReporter() { // Enable reportErrorsAsExceptions if (console._errorOriginal) { return; // already installed } // Flow doesn't like it when you set arbitrary values on a global object (console: any)._errorOriginal = console.error.bind(console); console.error = function reactConsoleError() { // Flow doesn't like it when you set arbitrary values on a global object (console: any)._errorOriginal.apply(null, arguments); if (!console.reportErrorsAsExceptions) { return; } if (arguments[0] && arguments[0].stack) { reportException(arguments[0], /* isFatal */ false); } else { const stringifySafe = require('stringifySafe'); const str = Array.prototype.map.call(arguments, stringifySafe).join(', '); if (str.slice(0, 10) === '"Warning: ') { // React warnings use console.error so that a stack trace is shown, but // we don't (currently) want these to show a redbox // (Note: Logic duplicated in polyfills/console.js.) return; } const error : any = new Error('console.error: ' + str); error.framesToPop = 1; reportException(error, /* isFatal */ false); } }; if (console.reportErrorsAsExceptions === undefined) { // Flow doesn't like it when you set arbitrary values on a global object (console: any).reportErrorsAsExceptions = true; // Individual apps can disable this } } module.exports = { handleException, installConsoleErrorReporter };