Skip to content

Modularize and extract crashOverlay to iframe #1842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 25, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,129 changes: 682 additions & 447 deletions packages/react-dev-utils/crashOverlay.js
Original file line number Diff line number Diff line change
@@ -9,88 +9,234 @@

'use strict';

var codeFrame = require('babel-code-frame');
function _interopDefault(ex) {
return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex;
}

var stackFrameParser = require('stack-frame-parser');
var stackFrameMapper = require('stack-frame-mapper');
var stackFrameUnmapper = require('stack-frame-unmapper');
var codeFrame = _interopDefault(require('babel-code-frame'));
var ansiHTML = require('./ansiHTML');
var StackTraceResolve = require('stack-frame-resolver').default;

var CONTEXT_SIZE = 3;
var boundErrorHandler = null;

var black = '#293238';
var darkGray = '#878e91';
var lightGray = '#fafafa';
var red = '#ce1126';
var lightRed = '#fccfcf';
var yellow = '#fbf5b4';
function errorHandler(callback, e) {
if (!e.error) {
return;
}
// $FlowFixMe
var error = e.error;

function getHead() {
return document.head || document.getElementsByTagName('head')[0];
if (error instanceof Error) {
callback(error);
} else {
// A non-error was thrown, we don't have a trace. :(
// Look in your browser's devtools for more information
callback(new Error(error));
}
}

var injectedCss = [];
function registerUnhandledError(target, callback) {
if (boundErrorHandler !== null) {
return;
}
boundErrorHandler = errorHandler.bind(undefined, callback);
target.addEventListener('error', boundErrorHandler);
}

// From: http://stackoverflow.com/a/524721/127629
function injectCss(css) {
var head = getHead();
var style = document.createElement('style');
function unregisterUnhandledError(target) {
if (boundErrorHandler === null) {
return;
}
target.removeEventListener('error', boundErrorHandler);
boundErrorHandler = null;
}

style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
var boundRejectionHandler = null;

function rejectionHandler(callback, e) {
if (e == null || e.reason == null) {
return callback(new Error('Unknown'));
}
var reason = e.reason;

if (reason instanceof Error) {
return callback(reason);
}
// A non-error was rejected, we don't have a trace :(
// Look in your browser's devtools for more information
return callback(new Error(reason));
}

function registerUnhandledRejection(target, callback) {
if (boundRejectionHandler !== null) {
return;
}
boundRejectionHandler = rejectionHandler.bind(undefined, callback);
// $FlowFixMe
target.addEventListener('unhandledrejection', boundRejectionHandler);
}

function unregisterUnhandledRejection(target) {
if (boundRejectionHandler === null) {
return;
}
// $FlowFixMe
target.removeEventListener('unhandledrejection', boundRejectionHandler);
boundRejectionHandler = null;
}

var SHORTCUT_ESCAPE = 'SHORTCUT_ESCAPE';
var SHORTCUT_LEFT = 'SHORTCUT_LEFT';
var SHORTCUT_RIGHT = 'SHORTCUT_RIGHT';

var boundKeyHandler = null;

function keyHandler(callback, e) {
var key = e.key, keyCode = e.keyCode, which = e.which;

if (key === 'Escape' || keyCode === 27 || which === 27) {
callback(SHORTCUT_ESCAPE);
} else if (key === 'ArrowLeft' || keyCode === 37 || which === 37) {
callback(SHORTCUT_LEFT);
} else if (key === 'ArrowRight' || keyCode === 39 || which === 39) {
callback(SHORTCUT_RIGHT);
}
}

function registerShortcuts(target, callback) {
if (boundKeyHandler !== null) {
return;
}
boundKeyHandler = keyHandler.bind(undefined, callback);
target.addEventListener('keydown', boundKeyHandler);
}

function unregisterShortcuts(target) {
if (boundKeyHandler === null) {
return;
}
target.removeEventListener('keydown', boundKeyHandler);
boundKeyHandler = null;
}

var stackTraceRegistered = false;
// Default: https://docs.microsoft.com/en-us/scripting/javascript/reference/stacktracelimit-property-error-javascript
var restoreStackTraceValue = 10;

var MAX_STACK_LENGTH = 50;

function registerStackTraceLimit() {
var limit = arguments.length > 0 && arguments[0] !== undefined
? arguments[0]
: MAX_STACK_LENGTH;

if (stackTraceRegistered) {
return;
}
try {
restoreStackTraceValue = Error.stackTraceLimit;
Error.stackTraceLimit = limit;
stackTraceRegistered = true;
} catch (e) {
// Not all browsers support this so we don't care if it errors
}
}

function unregisterStackTraceLimit() {
if (!stackTraceRegistered) {
return;
}
try {
Error.stackTraceLimit = restoreStackTraceValue;
stackTraceRegistered = false;
} catch (e) {
// Not all browsers support this so we don't care if it errors
}
}

var recorded = [];

var errorsConsumed = 0;

function consume(error) {
var unhandledRejection = arguments.length > 1 && arguments[1] !== undefined
? arguments[1]
: false;
var contextSize = arguments.length > 2 && arguments[2] !== undefined
? arguments[2]
: 3;

var parsedFrames = stackFrameParser.parse(error);
var enhancedFramesPromise = void 0;
if (error.__unmap_source) {
enhancedFramesPromise = stackFrameUnmapper.unmap(
error.__unmap_source,
parsedFrames,
contextSize
);
} else {
style.appendChild(document.createTextNode(css));
enhancedFramesPromise = stackFrameMapper.map(parsedFrames, contextSize);
}
return enhancedFramesPromise.then(function(enhancedFrames) {
enhancedFrames = enhancedFrames.filter(function(_ref) {
var functionName = _ref.functionName;
return functionName == null ||
functionName.indexOf('__stack_frame_overlay_proxy_console__') === -1;
});
recorded[++errorsConsumed] = {
error: error,
unhandledRejection: unhandledRejection,
contextSize: contextSize,
enhancedFrames: enhancedFrames,
};
return errorsConsumed;
});
}

head.appendChild(style);
injectedCss.push(style);
function getErrorRecord(ref) {
return recorded[ref];
}

var css = [
'.cra-container {',
' padding-right: 15px;',
' padding-left: 15px;',
' margin-right: auto;',
' margin-left: auto;',
'}',
'',
'@media (min-width: 768px) {',
' .cra-container {',
' width: calc(750px - 6em);',
' }',
'}',
'',
'@media (min-width: 992px) {',
' .cra-container {',
' width: calc(970px - 6em);',
' }',
'}',
'',
'@media (min-width: 1200px) {',
' .cra-container {',
' width: calc(1170px - 6em);',
' }',
'}',
].join('\n');
function drain() {
// $FlowFixMe
var keys = Object.keys(recorded);
for (var index = 0; index < keys.length; ++index) {
delete recorded[keys[index]];
}
}

var overlayStyle = {
var black = '#293238';
var darkGray = '#878e91';
var lightGray = '#fafafa';
var red = '#ce1126';
var lightRed = '#fccfcf';
var yellow = '#fbf5b4';

var iframeStyle = {
'background-color': lightGray,
position: 'fixed',
'box-sizing': 'border-box',
top: '1em',
left: '1em',
bottom: '1em',
right: '1em',
width: 'calc(100% - 2em)',
height: 'calc(100% - 2em)',
'border-radius': '3px',
'background-color': lightGray,
padding: '4rem',
'box-shadow': '0 0 6px 0 rgba(0, 0, 0, 0.5)',
'z-index': 1337,
};

var overlayStyle = {
'box-sizing': 'border-box',
padding: '4rem',
'font-family': 'Consolas, Menlo, monospace',
color: black,
'white-space': 'pre-wrap',
overflow: 'auto',
'overflow-x': 'hidden',
'word-break': 'break-all',
'box-shadow': '0 0 6px 0 rgba(0, 0, 0, 0.5)',
'word-break': 'break-word',
'line-height': 1.5,
};

@@ -220,220 +366,181 @@ var footerStyle = {
color: darkGray,
};

var injectedCount = 0;
var injectedCache = {};

function getHead(document) {
return document.head || document.getElementsByTagName('head')[0];
}

function injectCss(document, css) {
var head = getHead(document);
var style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
head.appendChild(style);

injectedCache[++injectedCount] = style;
return injectedCount;
}

function applyStyles(element, styles) {
element.setAttribute('style', '');
// Firefox can't handle const due to non-compliant implementation
// Revisit Jan 2016
// https://developer.mozilla.org/en-US/Firefox/Releases/51#JavaScript
// https://bugzilla.mozilla.org/show_bug.cgi?id=1101653
for (var key in styles) {
if (!styles.hasOwnProperty(key)) continue;
var val = styles[key];
if (typeof val === 'function') val = val();
element.style[key] = val.toString();
if (!styles.hasOwnProperty(key)) {
continue;
}
// $FlowFixMe
element.style[key] = styles[key];
}
}

var overlayReference = null;
var additionalReference = null;
var capturedErrors = [];
var viewIndex = -1;
var frameSettings = [];
function createHint(document, hint) {
var span = document.createElement('span');
span.appendChild(document.createTextNode(hint));
applyStyles(span, hintStyle);
return span;
}

function consumeEvent(e) {
e.preventDefault();
e.target.blur();
function createClose(document, callback) {
var hints = document.createElement('div');
applyStyles(hints, hintsStyle);

var close = createHint(document, '×');
close.addEventListener('click', function() {
return callback();
});
applyStyles(close, closeButtonStyle);
hints.appendChild(close);
return hints;
}

function accessify(node) {
node.setAttribute('tabindex', 0);
function enableTabClick(node) {
node.setAttribute('tabindex', '0');
node.addEventListener('keydown', function(e) {
var key = e.key, which = e.which, keyCode = e.keyCode;

if (key === 'Enter' || which === 13 || keyCode === 13) {
e.preventDefault();
e.target.click();
if (typeof e.target.click === 'function') {
e.target.click();
}
}
});
}

function renderAdditional() {
if (additionalReference.lastChild) {
additionalReference.removeChild(additionalReference.lastChild);
}

var text = ' ';
if (capturedErrors.length <= 1) {
additionalReference.appendChild(document.createTextNode(text));
return;
}
text = 'Errors ' + (viewIndex + 1) + ' of ' + capturedErrors.length;
var span = document.createElement('span');
span.appendChild(document.createTextNode(text));
var group = document.createElement('span');
applyStyles(group, groupStyle);
var left = document.createElement('button');
applyStyles(left, groupElemLeft);
left.addEventListener('click', function(e) {
consumeEvent(e);
switchError(-1);
});
left.appendChild(document.createTextNode('←'));
accessify(left);
var right = document.createElement('button');
applyStyles(right, groupElemRight);
right.addEventListener('click', function(e) {
consumeEvent(e);
switchError(1);
});
right.appendChild(document.createTextNode('→'));
accessify(right);
group.appendChild(left);
group.appendChild(right);
span.appendChild(group);
additionalReference.appendChild(span);
}

function removeNextBr(parent, component) {
while (component != null && component.tagName.toLowerCase() !== 'br') {
component = component.nextSibling;
component = component.nextElementSibling;
}
if (component != null) {
parent.removeChild(component);
}
}

function absolutifyCode(component) {
function absolutifyCaret(component) {
var ccn = component.childNodes;
for (var index = 0; index < ccn.length; ++index) {
var c = ccn[index];
if (c.tagName.toLowerCase() !== 'span') continue;
var text = c.innerText.replace(/\s/g, '');
if (text !== '|^') continue;
// $FlowFixMe
if (c.tagName.toLowerCase() !== 'span') {
continue;
}
var _text = c.innerText;
if (_text == null) {
continue;
}
var text = _text.replace(/\s/g, '');
if (text !== '|^') {
continue;
}
// $FlowFixMe
c.style.position = 'absolute';
// $FlowFixMe
removeNextBr(component, c);
}
}

function sourceCodePre(sourceLines, lineNum, columnNum) {
var main = arguments.length > 3 && arguments[3] !== undefined
? arguments[3]
function createCode(document, sourceLines, lineNum, columnNum, contextSize) {
var main = arguments.length > 5 && arguments[5] !== undefined
? arguments[5]
: false;

var sourceCode = [];
var whiteSpace = Infinity;
sourceLines.forEach(function(_ref2) {
var text = _ref2.text;
sourceLines.forEach(function(e) {
var text = e.content;

var m = text.match(/^\s*/);
if (text === '') return;
if (text === '') {
return;
}
if (m && m[0]) {
whiteSpace = Math.min(whiteSpace, m[0].length);
} else {
whiteSpace = 0;
}
});
sourceLines.forEach(function(_ref3) {
var text = _ref3.text, line = _ref3.line;
sourceLines.forEach(function(e) {
var text = e.content;
var line = e.lineNumber;

if (isFinite(whiteSpace)) text = text.substring(whiteSpace);
if (isFinite(whiteSpace)) {
text = text.substring(whiteSpace);
}
sourceCode[line - 1] = text;
});
sourceCode = sourceCode.join('\n');
var ansiHighlight = codeFrame(
sourceCode,
sourceCode.join('\n'),
lineNum,
columnNum - (isFinite(whiteSpace) ? whiteSpace : 0),
{
forceColor: true,
linesAbove: CONTEXT_SIZE,
linesBelow: CONTEXT_SIZE,
linesAbove: contextSize,
linesBelow: contextSize,
}
);
var htmlHighlight = ansiHTML(ansiHighlight);
var code = document.createElement('code');
code.innerHTML = htmlHighlight;
absolutifyCode(code);
absolutifyCaret(code);
applyStyles(code, codeStyle);

var ccn = code.childNodes;
for (var index = 0; index < ccn.length; ++index) {
oLoop: for (var index = 0; index < ccn.length; ++index) {
var node = ccn[index];
var breakOut = false;
var ccn2 = node.childNodes;
for (var index2 = 0; index2 < ccn2.length; ++index2) {
var lineNode = ccn2[index2];
if (lineNode.innerText.indexOf(' ' + lineNum + ' |') === -1) continue;
var text = lineNode.innerText;
if (text == null) {
continue;
}
if (text.indexOf(' ' + lineNum + ' |') === -1) {
continue;
}
// $FlowFixMe
applyStyles(node, main ? primaryErrorStyle : secondaryErrorStyle);
breakOut = true;
break oLoop;
}
if (breakOut) break;
}
var pre = document.createElement('pre');
applyStyles(pre, preStyle);
pre.appendChild(code);
return pre;
}

function createHint(hint) {
var span = document.createElement('span');
span.appendChild(document.createTextNode(hint));
applyStyles(span, hintStyle);
return span;
}

function hintsDiv() {
var hints = document.createElement('div');
applyStyles(hints, hintsStyle);

var close = createHint('×');
close.addEventListener('click', function() {
unmount();
});
applyStyles(close, closeButtonStyle);
hints.appendChild(close);
return hints;
}

function frameDiv(functionName, url, internalUrl) {
var frame = document.createElement('div');
var frameFunctionName = document.createElement('div');

var cleanedFunctionName = void 0;
if (!functionName || functionName === 'Object.<anonymous>') {
cleanedFunctionName = '(anonymous function)';
} else {
cleanedFunctionName = functionName;
}

var cleanedUrl = url.replace('webpack://', '.');

if (internalUrl) {
applyStyles(
frameFunctionName,
Object.assign({}, functionNameStyle, depStyle)
);
} else {
applyStyles(frameFunctionName, functionNameStyle);
}

frameFunctionName.appendChild(document.createTextNode(cleanedFunctionName));
frame.appendChild(frameFunctionName);

var frameLink = document.createElement('div');
applyStyles(frameLink, linkStyle);
var frameAnchor = document.createElement('a');
applyStyles(frameAnchor, anchorStyle);
//frameAnchor.href = url
frameAnchor.appendChild(document.createTextNode(cleanedUrl));
frameLink.appendChild(frameAnchor);
frame.appendChild(frameLink);

return frame;
function isInternalFile(url, sourceFileName) {
return url.indexOf('/~/') !== -1 ||
url.indexOf('/node_modules/') !== -1 ||
url.trim().indexOf(' ') !== -1 ||
sourceFileName == null ||
sourceFileName.length === 0;
}

function getGroupToggle(omitsCount, omitBundle) {
function getGroupToggle(document, omitsCount, omitBundle) {
var omittedFrames = document.createElement('div');
accessify(omittedFrames);
enableTabClick(omittedFrames);
var text1 = document.createTextNode(
'\u25B6 ' + omitsCount + ' stack frames were collapsed.'
);
@@ -461,15 +568,23 @@ function getGroupToggle(omitsCount, omitBundle) {
return omittedFrames;
}

function insertBeforeBundle(parent, omitsCount, omitBundle, actionElement) {
function insertBeforeBundle(
document,
parent,
omitsCount,
omitBundle,
actionElement
) {
var children = document.getElementsByName('bundle-' + omitBundle);
if (children.length < 1) return;
if (children.length < 1) {
return;
}
var first = children[0];
while (first.parentNode !== parent) {
while (first != null && first.parentNode !== parent) {
first = first.parentNode;
}
var div = document.createElement('div');
accessify(div);
enableTabClick(div);
div.setAttribute('name', 'bundle-' + omitBundle);
var text = document.createTextNode(
'\u25BC ' + omitsCount + ' stack frames were expanded.'
@@ -484,9 +599,48 @@ function insertBeforeBundle(parent, omitsCount, omitBundle, actionElement) {
parent.insertBefore(div, first);
}

function traceFrame(
function frameDiv(document, functionName, url, internalUrl) {
var frame = document.createElement('div');
var frameFunctionName = document.createElement('div');

var cleanedFunctionName = void 0;
if (!functionName || functionName === 'Object.<anonymous>') {
cleanedFunctionName = '(anonymous function)';
} else {
cleanedFunctionName = functionName;
}

var cleanedUrl = url.replace('webpack://', '.');

if (internalUrl) {
applyStyles(
frameFunctionName,
Object.assign({}, functionNameStyle, depStyle)
);
} else {
applyStyles(frameFunctionName, functionNameStyle);
}

frameFunctionName.appendChild(document.createTextNode(cleanedFunctionName));
frame.appendChild(frameFunctionName);

var frameLink = document.createElement('div');
applyStyles(frameLink, linkStyle);
var frameAnchor = document.createElement('a');
applyStyles(frameAnchor, anchorStyle);
//frameAnchor.href = url
frameAnchor.appendChild(document.createTextNode(cleanedUrl));
frameLink.appendChild(frameAnchor);
frame.appendChild(frameLink);

return frame;
}

function createFrame(
document,
frameSetting,
frame,
contextSize,
critical,
omits,
omitBundle,
@@ -498,19 +652,23 @@ function traceFrame(
fileName = frame.fileName,
lineNumber = frame.lineNumber,
columnNumber = frame.columnNumber,
scriptLines = frame.scriptLines,
sourceFileName = frame.sourceFileName,
sourceLineNumber = frame.sourceLineNumber,
sourceColumnNumber = frame.sourceColumnNumber,
sourceLines = frame.sourceLines;
scriptLines = frame._scriptCode,
sourceFileName = frame._originalFileName,
sourceLineNumber = frame._originalLineNumber,
sourceColumnNumber = frame._originalColumnNumber,
sourceLines = frame._originalScriptCode;

var url = void 0;
if (!compiled && sourceFileName) {
url = sourceFileName + ':' + sourceLineNumber;
if (sourceColumnNumber) url += ':' + sourceColumnNumber;
if (sourceColumnNumber) {
url += ':' + sourceColumnNumber;
}
} else {
url = fileName + ':' + lineNumber;
if (columnNumber) url += ':' + columnNumber;
if (columnNumber) {
url += ':' + columnNumber;
}
}

var needsHidden = false;
@@ -522,19 +680,17 @@ function traceFrame(
var collapseElement = null;
if (!internalUrl || lastElement) {
if (omits.value > 0) {
var omittedFrames = getGroupToggle(omits.value, omitBundle);
setTimeout(
(function() {
insertBeforeBundle.apply(undefined, arguments);
}).bind(
undefined,
var capV = omits.value;
var omittedFrames = getGroupToggle(document, capV, omitBundle);
window.requestAnimationFrame(function() {
insertBeforeBundle(
document,
parentContainer,
omits.value,
capV,
omitBundle,
omittedFrames
),
1
);
);
});
if (lastElement && internalUrl) {
collapseElement = omittedFrames;
} else {
@@ -545,7 +701,7 @@ function traceFrame(
omits.value = 0;
}

var elem = frameDiv(functionName, url, internalUrl);
var elem = frameDiv(document, functionName, url, internalUrl);
if (needsHidden) {
applyStyles(elem, hiddenStyle);
elem.setAttribute('name', 'bundle-' + omitBundle);
@@ -555,15 +711,24 @@ function traceFrame(
if (!internalUrl) {
if (compiled && scriptLines.length !== 0) {
elem.appendChild(
sourceCodePre(scriptLines, lineNumber, columnNumber, critical)
createCode(
document,
scriptLines,
lineNumber,
columnNumber,
contextSize,
critical
)
);
hasSource = true;
} else if (!compiled && sourceLines.length !== 0) {
elem.appendChild(
sourceCodePre(
createCode(
document,
sourceLines,
sourceLineNumber,
sourceColumnNumber,
contextSize,
critical
)
);
@@ -574,9 +739,18 @@ function traceFrame(
return { elem: elem, hasSource: hasSource, collapseElement: collapseElement };
}

function lazyFrame(parent, factory, lIndex) {
function createFrameWrapper(
document,
parent,
factory,
lIndex,
frameSettings,
contextSize
) {
var fac = factory();
if (fac == null) return;
if (fac == null) {
return;
}
var hasSource = fac.hasSource,
elem = fac.elem,
collapseElement = fac.collapseElement;
@@ -585,27 +759,34 @@ function lazyFrame(parent, factory, lIndex) {
elemWrapper.appendChild(elem);

if (hasSource) {
(function() {
var compiledDiv = document.createElement('div');
accessify(compiledDiv);
applyStyles(compiledDiv, toggleStyle);

var o = frameSettings[lIndex];
var compiledText = document.createTextNode(
'View ' + (o && o.compiled ? 'source' : 'compiled')
);
compiledDiv.addEventListener('click', function() {
if (o) o.compiled = !o.compiled;
var compiledDiv = document.createElement('div');
enableTabClick(compiledDiv);
applyStyles(compiledDiv, toggleStyle);

var next = lazyFrame(parent, factory, lIndex);
if (next != null) {
parent.insertBefore(next, elemWrapper);
parent.removeChild(elemWrapper);
}
});
compiledDiv.appendChild(compiledText);
elemWrapper.appendChild(compiledDiv);
})();
var o = frameSettings[lIndex];
var compiledText = document.createTextNode(
'View ' + (o && o.compiled ? 'source' : 'compiled')
);
compiledDiv.addEventListener('click', function() {
if (o) {
o.compiled = !o.compiled;
}

var next = createFrameWrapper(
document,
parent,
factory,
lIndex,
frameSettings,
contextSize
);
if (next != null) {
parent.insertBefore(next, elemWrapper);
parent.removeChild(elemWrapper);
}
});
compiledDiv.appendChild(compiledText);
elemWrapper.appendChild(compiledDiv);
}

if (collapseElement != null) {
@@ -615,7 +796,12 @@ function lazyFrame(parent, factory, lIndex) {
return elemWrapper;
}

function traceDiv(resolvedFrames) {
function createFrames(document, resolvedFrames, frameSettings, contextSize) {
if (resolvedFrames.length !== frameSettings.length) {
throw new Error(
'You must give a frame settings array of identical length to resolved frames.'
);
}
var trace = document.createElement('div');
applyStyles(trace, traceStyle);

@@ -624,21 +810,28 @@ function traceDiv(resolvedFrames) {
var omits = { value: 0, bundle: 1 };
resolvedFrames.forEach(function(frame) {
var lIndex = index++;
var elem = lazyFrame(
var elem = createFrameWrapper(
document,
trace,
traceFrame.bind(
createFrame.bind(
undefined,
document,
frameSettings[lIndex],
frame,
contextSize,
critical,
omits,
omits.bundle,
trace,
index === resolvedFrames.length
),
lIndex
lIndex,
frameSettings,
contextSize
);
if (elem == null) return;
if (elem == null) {
return;
}
critical = false;
trace.appendChild(elem);
});
@@ -648,7 +841,7 @@ function traceDiv(resolvedFrames) {
return trace;
}

function footer() {
function createFooter(document) {
var div = document.createElement('div');
applyStyles(div, footerStyle);
div.appendChild(
@@ -665,30 +858,91 @@ function footer() {
return div;
}

function render(error, name, message, resolvedFrames) {
dispose();
function consumeEvent(e) {
e.preventDefault();
if (typeof e.target.blur === 'function') {
e.target.blur();
}
}

frameSettings = resolvedFrames.map(function() {
return { compiled: false };
});
function updateAdditional(
document,
additionalReference,
currentError,
totalErrors,
switchCallback
) {
if (additionalReference.lastChild) {
additionalReference.removeChild(additionalReference.lastChild);
}

injectCss(css);
var text = ' ';
if (totalErrors <= 1) {
additionalReference.appendChild(document.createTextNode(text));
return;
}
text = 'Errors ' + currentError + ' of ' + totalErrors;
var span = document.createElement('span');
span.appendChild(document.createTextNode(text));
var group = document.createElement('span');
applyStyles(group, groupStyle);
var left = document.createElement('button');
applyStyles(left, groupElemLeft);
left.addEventListener('click', function(e) {
consumeEvent(e);
switchCallback(-1);
});
left.appendChild(document.createTextNode('←'));
enableTabClick(left);
var right = document.createElement('button');
applyStyles(right, groupElemRight);
right.addEventListener('click', function(e) {
consumeEvent(e);
switchCallback(1);
});
right.appendChild(document.createTextNode('→'));
enableTabClick(right);
group.appendChild(left);
group.appendChild(right);
span.appendChild(group);
additionalReference.appendChild(span);
}

function createOverlay(
document,
name,
message,
frames,
contextSize,
currentError,
totalErrors,
switchCallback,
closeCallback
) {
var frameSettings = frames.map(function() {
return { compiled: false };
});
// Create overlay
var overlay = document.createElement('div');
applyStyles(overlay, overlayStyle);
overlay.appendChild(hintsDiv());
overlay.appendChild(createClose(document, closeCallback));

// Create container
var container = document.createElement('div');
container.className = 'cra-container';
overlay.appendChild(container);

// Create additional
additionalReference = document.createElement('div');
applyStyles(additionalReference, additionalStyle);
container.appendChild(additionalReference);
renderAdditional();
var additional = document.createElement('div');
applyStyles(additional, additionalStyle);
container.appendChild(additional);
updateAdditional(
document,
additional,
currentError,
totalErrors,
switchCallback
);

// Create header
var header = document.createElement('div');
@@ -701,235 +955,216 @@ function render(error, name, message, resolvedFrames) {
container.appendChild(header);

// Create trace
container.appendChild(traceDiv(resolvedFrames));
container.appendChild(
createFrames(document, frames, frameSettings, contextSize)
);

// Show message
container.appendChild(footer());
container.appendChild(createFooter(document));

// Mount
document.body.appendChild((overlayReference = overlay));
return {
overlay: overlay,
additional: additional,
};
}

function dispose() {
if (overlayReference === null) return;
document.body.removeChild(overlayReference);
overlayReference = null;
var head = getHead();
injectedCss.forEach(function(node) {
head.removeChild(node);
});
injectedCss = [];
}
var CONTEXT_SIZE = 3;
var iframeReference = null;
var additionalReference = null;
var errorReferences = [];
var currReferenceIndex = -1;

function unmount() {
dispose();
capturedErrors = [];
viewIndex = -1;
}
var css = [
'.cra-container {',
' padding-right: 15px;',
' padding-left: 15px;',
' margin-right: auto;',
' margin-left: auto;',
'}',
'',
'@media (min-width: 768px) {',
' .cra-container {',
' width: calc(750px - 6em);',
' }',
'}',
'',
'@media (min-width: 992px) {',
' .cra-container {',
' width: calc(970px - 6em);',
' }',
'}',
'',
'@media (min-width: 1200px) {',
' .cra-container {',
' width: calc(1170px - 6em);',
' }',
'}',
].join('\n');

function isInternalFile(url, sourceFileName) {
return url.indexOf('/~/') !== -1 ||
url.trim().indexOf(' ') !== -1 ||
!sourceFileName;
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) {
keyHandler(
function(type) {
return shortcutHandler(type);
},
event
);
};
}
injectCss(iframeReference.contentDocument, css);
if (document.body != null) {
document.body.appendChild(overlay);
}
additionalReference = additional;
};
window.document.body.appendChild(iframe);
}

function renderError(index) {
viewIndex = index;
var _capturedErrors$index = capturedErrors[index],
error = _capturedErrors$index.error,
unhandledRejection = _capturedErrors$index.unhandledRejection,
resolvedFrames = _capturedErrors$index.resolvedFrames;
function renderErrorByIndex(index) {
currReferenceIndex = index;

var _getErrorRecord = getErrorRecord(errorReferences[index]),
error = _getErrorRecord.error,
unhandledRejection = _getErrorRecord.unhandledRejection,
enhancedFrames = _getErrorRecord.enhancedFrames;

if (unhandledRejection) {
render(
error,
'Unhandled Rejection (' + error.name + ')',
error.message,
resolvedFrames
enhancedFrames
);
} else {
render(error, error.name, error.message, resolvedFrames);
render(error.name, error.message, enhancedFrames);
}
}

function switchError(offset) {
var nextView = currReferenceIndex + offset;
if (nextView < 0 || nextView >= errorReferences.length) {
return;
}
renderErrorByIndex(nextView);
}

function disposeCurrentView() {
if (iframeReference === null) {
return;
}
window.document.body.removeChild(iframeReference);
iframeReference = null;
additionalReference = null;
}

function unmount() {
disposeCurrentView();
drain();
errorReferences = [];
currReferenceIndex = -1;
}

function crash(error) {
var unhandledRejection = arguments.length > 1 && arguments[1] !== undefined
? arguments[1]
: false;
var sourceOverrides = arguments.length > 2 && arguments[2] !== undefined
? arguments[2]
: [];

if (module.hot) module.hot.decline();

StackTraceResolve(error, CONTEXT_SIZE)
.then(function(resolvedFrames) {
resolvedFrames = resolvedFrames.filter(function(_ref) {
var functionName = _ref.functionName;
return functionName.indexOf('__cra_proxy_console__') === -1;
});
var overrideCount = sourceOverrides.length,
frameCount = resolvedFrames.length;
var frameIndex = 0;
for (
var overrideIndex = 0;
overrideIndex < overrideCount;
++overrideIndex
) {
var tag = sourceOverrides[overrideIndex];
var shouldContinue = false;
for (; frameIndex < frameCount; ++frameIndex) {
var sourceFileName = resolvedFrames[frameIndex].sourceFileName;

if (sourceFileName == null) continue;
if (sourceFileName.indexOf('/' + tag.file) !== -1) {
var prevLineNumber = resolvedFrames[frameIndex].sourceLineNumber;
if (Math.abs(prevLineNumber - tag.lineNum) < CONTEXT_SIZE) {
resolvedFrames[frameIndex].sourceLineNumber = tag.lineNum;
}
shouldContinue = true;
break;
if (module.hot && typeof module.hot.decline === 'function') {
module.hot.decline();
}
consume(error, unhandledRejection, CONTEXT_SIZE)
.then(function(ref) {
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.');
}
if (shouldContinue) continue;
break;
}
capturedErrors.push({
error: error,
unhandledRejection: unhandledRejection,
resolvedFrames: resolvedFrames,
});
if (overlayReference !== null)
renderAdditional();
else {
renderError((viewIndex = 0));
renderErrorByIndex((currReferenceIndex = 0));
}
})
.catch(function(e) {
// This is another fail case (unlikely to happen)
// e.g. render(...) throws an error with provided arguments
console.log('Red box renderer error:', e);
unmount();
render(
null,
'Error',
'There is an error with red box. *Please* report this (see console).',
[]
);
console.log('Could not consume error:', e);
});
}

function switchError(offset) {
try {
var nextView = viewIndex + offset;
if (nextView < 0 || nextView >= capturedErrors.length) return;
renderError(nextView);
} catch (e) {
console.log('Red box renderer error:', e);
unmount();
render(
null,
'Error',
'There is an error with red box. *Please* report this (see console).',
[]
);
}
}

window.onerror = function(messageOrEvent, source, lineno, colno, error) {
if (
error == null ||
!(error instanceof Error) ||
messageOrEvent.indexOf('Script error') !== -1
) {
crash(new Error(error || messageOrEvent)); // TODO: more helpful message
} else {
crash(error);
}
};

var promiseHandler = function promiseHandler(event) {
if (event != null && event.reason != null) {
var reason = event.reason;

if (reason == null || !(reason instanceof Error)) {
crash(new Error(reason), true);
} else {
crash(reason, true);
function shortcutHandler(type) {
switch (type) {
case SHORTCUT_ESCAPE: {
unmount();
break;
}
case SHORTCUT_LEFT: {
switchError(-1);
break;
}
case SHORTCUT_RIGHT: {
switchError(1);
break;
}
} else {
crash(new Error('Unknown event'), true);
}
};

window.addEventListener('unhandledrejection', promiseHandler);

var escapeHandler = function escapeHandler(event) {
var key = event.key, keyCode = event.keyCode, which = event.which;

if (key === 'Escape' || keyCode === 27 || which === 27) unmount();
else if (key === 'ArrowLeft' || keyCode === 37 || which === 37)
switchError(-1);
else if (key === 'ArrowRight' || keyCode === 39 || which === 39)
switchError(1);
};
}

window.addEventListener('keydown', escapeHandler);

try {
Error.stackTraceLimit = 50;
} catch (e) {
// Browser may not support this, we don't care.
}

// eslint-disable-next-line
var proxyConsole = function proxyConsole(type) {
var orig = console[type];
console[type] = function __cra_proxy_console__() {
var warning = [].slice.call(arguments).join(' ');
var nIndex = warning.indexOf('\n');
var message = warning;
if (nIndex !== -1) message = message.substring(0, nIndex);
var stack = warning
.substring(nIndex + 1)
.split('\n')
.filter(function(line) {
return line.indexOf('(at ') !== -1;
})
.map(function(line) {
var prefix = '(at ';
var suffix = ')';
line = line.substring(line.indexOf(prefix) + prefix.length);
line = line.substring(0, line.indexOf(suffix));
var parts = line.split(/[:]/g);
if (parts.length !== 2) return null;
var file = parts[0];
var lineNum = Number(parts[1]);
if (isNaN(lineNum)) return null;
return { file: file, lineNum: lineNum };
})
.filter(function(obj) {
return obj !== null;
});
var error = void 0;
try {
throw new Error(message);
} catch (e) {
error = e;
}
setTimeout(function() {
return crash(error, false, stack);
});
return orig.apply(this, arguments);
};
};
function inject() {
registerUnhandledError(window, function(error) {
return crash(error);
});
registerUnhandledRejection(window, function(error) {
return crash(error, true);
});
registerShortcuts(window, shortcutHandler);
registerStackTraceLimit();
}

// proxyConsole('error');
function uninject() {
unregisterStackTraceLimit();
unregisterShortcuts(window);
unregisterUnhandledRejection(window);
unregisterUnhandledError(window);
}

if (module.hot) {
inject();
if (module.hot && typeof module.hot.dispose === 'function') {
module.hot.dispose(function() {
unmount();
window.removeEventListener('unhandledrejection', promiseHandler);
window.removeEventListener('keydown', escapeHandler);
uninject();
});
}
4 changes: 3 additions & 1 deletion packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
@@ -36,7 +36,9 @@
"opn": "4.0.2",
"recursive-readdir": "2.1.1",
"sockjs-client": "1.1.2",
"stack-frame-resolver": "0.1.3",
"stack-frame-mapper": "0.4.0",
"stack-frame-parser": "0.4.0",
"stack-frame-unmapper": "0.4.0",
"strip-ansi": "3.0.1"
}
}