200 lines
5.6 KiB
JavaScript
200 lines
5.6 KiB
JavaScript
/**
|
|
* 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.
|
|
*/
|
|
|
|
import { register as registerError, unregister as unregisterError } from './effects/unhandledError';
|
|
import { register as registerPromise, unregister as unregisterPromise } from './effects/unhandledRejection';
|
|
import { register as registerShortcuts, unregister as unregisterShortcuts, handler as keyEventHandler, SHORTCUT_ESCAPE, SHORTCUT_LEFT, SHORTCUT_RIGHT } from './effects/shortcuts';
|
|
import { register as registerStackTraceLimit, unregister as unregisterStackTraceLimit } from './effects/stackTraceLimit';
|
|
import { permanentRegister as permanentRegisterConsole, registerReactStack, unregisterReactStack } from './effects/proxyConsole';
|
|
import { massage as massageWarning } from './utils/warnings';
|
|
|
|
import { consume as consumeError, getErrorRecord, drain as drainErrors } from './utils/errorRegister';
|
|
|
|
import { iframeStyle } from './styles';
|
|
import { applyStyles } from './utils/dom/css';
|
|
import { createOverlay } from './components/overlay';
|
|
import { updateAdditional } from './components/additional';
|
|
|
|
var CONTEXT_SIZE = 3;
|
|
var iframeReference = null;
|
|
var additionalReference = null;
|
|
var errorReferences = [];
|
|
var currReferenceIndex = -1;
|
|
|
|
function render(name, message, resolvedFrames) {
|
|
disposeCurrentView();
|
|
|
|
var iframe = window.document.createElement('iframe');
|
|
applyStyles(iframe, iframeStyle);
|
|
iframeReference = iframe;
|
|
iframe.onload = function () {
|
|
if (iframeReference == null) {
|
|
return;
|
|
}
|
|
var w = iframeReference.contentWindow;
|
|
var document = iframeReference.contentDocument;
|
|
|
|
var _createOverlay = createOverlay(document, name, message, resolvedFrames, CONTEXT_SIZE, currReferenceIndex + 1, errorReferences.length, function (offset) {
|
|
switchError(offset);
|
|
}, function () {
|
|
unmount();
|
|
}),
|
|
overlay = _createOverlay.overlay,
|
|
additional = _createOverlay.additional;
|
|
|
|
if (w != null) {
|
|
w.onkeydown = function (event) {
|
|
keyEventHandler(function (type) {
|
|
return shortcutHandler(type);
|
|
}, event);
|
|
};
|
|
}
|
|
if (document.body != null) {
|
|
document.body.style.margin = '0';
|
|
// Keep popup within body boundaries for iOS Safari
|
|
// $FlowFixMe
|
|
document.body.style['max-width'] = '100vw';
|
|
|
|
document.body.appendChild(overlay);
|
|
}
|
|
additionalReference = additional;
|
|
};
|
|
window.document.body.appendChild(iframe);
|
|
}
|
|
|
|
function renderErrorByIndex(index) {
|
|
currReferenceIndex = index;
|
|
|
|
var _getErrorRecord = getErrorRecord(errorReferences[index]),
|
|
error = _getErrorRecord.error,
|
|
unhandledRejection = _getErrorRecord.unhandledRejection,
|
|
enhancedFrames = _getErrorRecord.enhancedFrames;
|
|
|
|
if (unhandledRejection) {
|
|
render('Unhandled Rejection (' + error.name + ')', error.message, enhancedFrames);
|
|
} else {
|
|
render(error.name, error.message, enhancedFrames);
|
|
}
|
|
}
|
|
|
|
function switchError(offset) {
|
|
if (errorReferences.length === 0) {
|
|
return;
|
|
}
|
|
|
|
var nextView = currReferenceIndex + offset;
|
|
|
|
if (nextView < 0) {
|
|
nextView = errorReferences.length - 1;
|
|
} else if (nextView >= errorReferences.length) {
|
|
nextView = 0;
|
|
}
|
|
|
|
renderErrorByIndex(nextView);
|
|
}
|
|
|
|
function disposeCurrentView() {
|
|
if (iframeReference === null) {
|
|
return;
|
|
}
|
|
window.document.body.removeChild(iframeReference);
|
|
iframeReference = null;
|
|
additionalReference = null;
|
|
}
|
|
|
|
function unmount() {
|
|
disposeCurrentView();
|
|
drainErrors();
|
|
errorReferences = [];
|
|
currReferenceIndex = -1;
|
|
}
|
|
|
|
function crash(error) {
|
|
var unhandledRejection = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
|
|
if (module.hot && typeof module.hot.decline === 'function') {
|
|
module.hot.decline();
|
|
}
|
|
consumeError(error, unhandledRejection, CONTEXT_SIZE).then(function (ref) {
|
|
if (ref == null) {
|
|
return;
|
|
}
|
|
errorReferences.push(ref);
|
|
if (iframeReference !== null && additionalReference !== null) {
|
|
updateAdditional(iframeReference.contentDocument, additionalReference, currReferenceIndex + 1, errorReferences.length, function (offset) {
|
|
switchError(offset);
|
|
});
|
|
} else {
|
|
if (errorReferences.length !== 1) {
|
|
throw new Error('Something is *really* wrong.');
|
|
}
|
|
renderErrorByIndex(currReferenceIndex = 0);
|
|
}
|
|
}).catch(function (e) {
|
|
console.log('Could not consume error:', e);
|
|
});
|
|
}
|
|
|
|
function shortcutHandler(type) {
|
|
switch (type) {
|
|
case SHORTCUT_ESCAPE:
|
|
{
|
|
unmount();
|
|
break;
|
|
}
|
|
case SHORTCUT_LEFT:
|
|
{
|
|
switchError(-1);
|
|
break;
|
|
}
|
|
case SHORTCUT_RIGHT:
|
|
{
|
|
switchError(1);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
//TODO: this
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function inject() {
|
|
registerError(window, function (error) {
|
|
return crash(error);
|
|
});
|
|
registerPromise(window, function (error) {
|
|
return crash(error, true);
|
|
});
|
|
registerShortcuts(window, shortcutHandler);
|
|
registerStackTraceLimit();
|
|
|
|
registerReactStack();
|
|
permanentRegisterConsole('error', function (warning, stack) {
|
|
var data = massageWarning(warning, stack);
|
|
crash(
|
|
// $FlowFixMe
|
|
{
|
|
message: data.message,
|
|
stack: data.stack,
|
|
__unmap_source: '/static/js/bundle.js'
|
|
}, false);
|
|
});
|
|
}
|
|
|
|
function uninject() {
|
|
unregisterStackTraceLimit();
|
|
unregisterShortcuts(window);
|
|
unregisterPromise(window);
|
|
unregisterError(window);
|
|
unregisterReactStack();
|
|
}
|
|
|
|
export { inject, uninject }; |