diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js new file mode 100644 index 000000000..3b3a20e4e --- /dev/null +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -0,0 +1,71 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule ExceptionsManager + */ +'use strict'; + +var Platform = require('Platform'); +var RCTExceptionsManager = require('NativeModules').ExceptionsManager; + +var loadSourceMap = require('loadSourceMap'); +var parseErrorStack = require('parseErrorStack'); + +var sourceMapPromise; + +function handleException(e) { + var stack = parseErrorStack(e); + console.error( + 'Error: ' + + '\n stack: \n' + stackToString(stack) + + '\n URL: ' + e.sourceURL + + '\n line: ' + e.line + + '\n message: ' + e.message + ); + + if (RCTExceptionsManager) { + RCTExceptionsManager.reportUnhandledException(e.message, format(stack)); + if (__DEV__) { + (sourceMapPromise = sourceMapPromise || loadSourceMap()) + .then(map => { + var prettyStack = parseErrorStack(e, map); + RCTExceptionsManager.updateExceptionMessage(e.message, format(prettyStack)); + }) + .then(null, error => { + console.error('#CLOWNTOWN (error while displaying error): ' + error.message); + }); + } + } +} + +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(' '); +} + +// HACK(frantic) Android currently expects stack trace to be a string #5920439 +function format(stack) { + if (Platform.OS === 'android') { + return stackToString(stack); + } else { + return stack; + } +} + +module.exports = { handleException }; diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 92c9d27ed..e011ac089 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -57,33 +57,11 @@ function setupDocumentShim() { } } -var sourceMapPromise; - function handleErrorWithRedBox(e) { - var RCTExceptionsManager = require('NativeModules').ExceptionsManager; - var errorToString = require('errorToString'); - var loadSourceMap = require('loadSourceMap'); - - GLOBAL.console.error( - 'Error: ' + - '\n stack: \n' + e.stack + - '\n URL: ' + e.sourceURL + - '\n line: ' + e.line + - '\n message: ' + e.message - ); - - if (RCTExceptionsManager) { - RCTExceptionsManager.reportUnhandledException(e.message, errorToString(e)); - if (__DEV__) { - (sourceMapPromise = sourceMapPromise || loadSourceMap()) - .then(map => { - var prettyStack = errorToString(e, map); - RCTExceptionsManager.updateExceptionMessage(e.message, prettyStack); - }) - .then(null, error => { - GLOBAL.console.error('#CLOWNTOWN (error while displaying error): ' + error.message); - }); - } + try { + require('ExceptionsManager').handleException(e); + } catch(ee) { + console.log('Failed to print error: ', ee.message); } } diff --git a/Libraries/JavaScriptAppEngine/Initialization/errorToString.js b/Libraries/JavaScriptAppEngine/Initialization/parseErrorStack.js similarity index 55% rename from Libraries/JavaScriptAppEngine/Initialization/errorToString.js rename to Libraries/JavaScriptAppEngine/Initialization/parseErrorStack.js index 652d6c2af..85a836705 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/errorToString.js +++ b/Libraries/JavaScriptAppEngine/Initialization/parseErrorStack.js @@ -1,21 +1,12 @@ /** * Copyright 2004-present Facebook. All Rights Reserved. * - * @providesModule errorToString + * @providesModule parseErrorStack */ 'use strict'; -var Platform = require('Platform'); - var stacktraceParser = require('stacktrace-parser'); -function stackFrameToString(stackFrame) { - var fileNameParts = stackFrame.file.split('/'); - var fileName = fileNameParts[fileNameParts.length - 1]; - - return stackFrame.methodName + '\n in ' + fileName + ':' + stackFrame.lineNumber + '\n'; -} - function resolveSourceMaps(sourceMapInstance, stackFrame) { try { var orig = sourceMapInstance.originalPositionFor({ @@ -31,7 +22,7 @@ function resolveSourceMaps(sourceMapInstance, stackFrame) { } } -function errorToString(e, sourceMapInstance) { +function parseErrorStack(e, sourceMapInstance) { var stack = stacktraceParser.parse(e.stack); var framesToPop = e.framesToPop || 0; @@ -43,12 +34,7 @@ function errorToString(e, sourceMapInstance) { stack.forEach(resolveSourceMaps.bind(null, sourceMapInstance)); } - // HACK(frantic) Android currently expects stack trace to be a string #5920439 - if (Platform.OS === 'android') { - return stack.map(stackFrameToString).join('\n'); - } else { - return stack; - } + return stack; } -module.exports = errorToString; +module.exports = parseErrorStack;