2015-03-19 16:51:33 +00:00
|
|
|
/**
|
2015-03-23 20:35:08 +00:00
|
|
|
* 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.
|
2015-03-19 16:51:33 +00:00
|
|
|
*
|
|
|
|
* @providesModule ExceptionsManager
|
2015-03-26 17:06:50 +00:00
|
|
|
* @flow
|
2015-03-19 16:51:33 +00:00
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var RCTExceptionsManager = require('NativeModules').ExceptionsManager;
|
|
|
|
|
|
|
|
var loadSourceMap = require('loadSourceMap');
|
|
|
|
var parseErrorStack = require('parseErrorStack');
|
2015-04-28 22:41:33 +00:00
|
|
|
var stringifySafe = require('stringifySafe');
|
2015-03-19 16:51:33 +00:00
|
|
|
|
|
|
|
var sourceMapPromise;
|
|
|
|
|
2015-03-26 17:06:50 +00:00
|
|
|
type Exception = {
|
|
|
|
sourceURL: string;
|
|
|
|
line: number;
|
|
|
|
message: string;
|
|
|
|
}
|
|
|
|
|
2015-07-15 11:01:38 +00:00
|
|
|
var exceptionID = 0;
|
|
|
|
|
2015-05-13 17:56:09 +00:00
|
|
|
function reportException(e: Exception, isFatal: bool, stack?: any) {
|
2015-07-15 11:01:38 +00:00
|
|
|
var currentExceptionID = ++exceptionID;
|
2015-03-19 16:51:33 +00:00
|
|
|
if (RCTExceptionsManager) {
|
2015-04-28 22:41:33 +00:00
|
|
|
if (!stack) {
|
|
|
|
stack = parseErrorStack(e);
|
|
|
|
}
|
2015-06-02 14:28:56 +00:00
|
|
|
if (isFatal) {
|
2015-07-15 11:01:38 +00:00
|
|
|
RCTExceptionsManager.reportFatalException(e.message, stack, currentExceptionID);
|
2015-05-13 17:56:09 +00:00
|
|
|
} else {
|
2015-06-02 14:28:56 +00:00
|
|
|
RCTExceptionsManager.reportSoftException(e.message, stack);
|
2015-05-13 17:56:09 +00:00
|
|
|
}
|
2015-03-19 16:51:33 +00:00
|
|
|
if (__DEV__) {
|
|
|
|
(sourceMapPromise = sourceMapPromise || loadSourceMap())
|
|
|
|
.then(map => {
|
|
|
|
var prettyStack = parseErrorStack(e, map);
|
2015-07-15 11:01:38 +00:00
|
|
|
RCTExceptionsManager.updateExceptionMessage(e.message, prettyStack, currentExceptionID);
|
2015-03-19 16:51:33 +00:00
|
|
|
})
|
2015-05-14 18:53:22 +00:00
|
|
|
.catch(error => {
|
|
|
|
// This can happen in a variety of normal situations, such as
|
|
|
|
// Network module not being available, or when running locally
|
|
|
|
console.warn('Unable to load source map: ' + error.message);
|
2015-03-19 16:51:33 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 17:56:09 +00:00
|
|
|
function handleException(e: Exception, isFatal: boolean) {
|
2015-04-28 22:41:33 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-05-13 17:56:09 +00:00
|
|
|
reportException(e, isFatal, stack);
|
2015-04-28 22:41:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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() {
|
2015-08-17 21:56:10 +00:00
|
|
|
// Note that when using the built-in context executor on iOS (i.e., not
|
|
|
|
// Chrome debugging), console.error is already stubbed out to cause a
|
|
|
|
// redbox via RCTNativeLoggingHook.
|
2015-04-28 22:41:33 +00:00
|
|
|
console.errorOriginal.apply(null, arguments);
|
|
|
|
if (!console.reportErrorsAsExceptions) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var str = Array.prototype.map.call(arguments, stringifySafe).join(', ');
|
2015-07-24 00:50:16 +00:00
|
|
|
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
|
2015-08-17 21:56:10 +00:00
|
|
|
// (Note: Logic duplicated in polyfills/console.js.)
|
2015-07-24 00:50:16 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-04-28 22:41:33 +00:00
|
|
|
var error: any = new Error('console.error: ' + str);
|
|
|
|
error.framesToPop = 1;
|
2015-05-13 17:56:09 +00:00
|
|
|
reportException(error, /* isFatal */ false);
|
2015-04-28 22:41:33 +00:00
|
|
|
};
|
|
|
|
if (console.reportErrorsAsExceptions === undefined) {
|
|
|
|
console.reportErrorsAsExceptions = true; // Individual apps can disable this
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-19 16:51:33 +00:00
|
|
|
function stackToString(stack) {
|
|
|
|
var maxLength = Math.max.apply(null, stack.map(frame => frame.methodName.length));
|
|
|
|
return stack.map(frame => stackFrameToString(frame, maxLength)).join('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
function stackFrameToString(stackFrame, maxLength) {
|
|
|
|
var fileNameParts = stackFrame.file.split('/');
|
|
|
|
var fileName = fileNameParts[fileNameParts.length - 1];
|
|
|
|
|
|
|
|
if (fileName.length > 18) {
|
|
|
|
fileName = fileName.substr(0, 17) + '\u2026' /* ... */;
|
|
|
|
}
|
|
|
|
|
|
|
|
var spaces = fillSpaces(maxLength - stackFrame.methodName.length);
|
|
|
|
return ' ' + stackFrame.methodName + spaces + ' ' + fileName + ':' + stackFrame.lineNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
function fillSpaces(n) {
|
|
|
|
return new Array(n + 1).join(' ');
|
|
|
|
}
|
|
|
|
|
2015-04-28 22:41:33 +00:00
|
|
|
module.exports = { handleException, installConsoleErrorReporter };
|