Commit d721d4a0 authored by Joe Haddad's avatar Joe Haddad Committed by GitHub
Browse files

Modularize and extract crashOverlay to iframe (#1842)

* Modularize and extract crashOverlay to iframe

* Do not break words

* Disable warning overlay

* Use CJS imports
parent 927c539b
Showing with 685 additions and 448 deletions
+685 -448
...@@ -9,71 +9,214 @@ ...@@ -9,71 +9,214 @@
'use strict'; '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 ansiHTML = require('./ansiHTML');
var StackTraceResolve = require('stack-frame-resolver').default;
var CONTEXT_SIZE = 3; var boundErrorHandler = null;
var black = '#293238'; function errorHandler(callback, e) {
var darkGray = '#878e91'; if (!e.error) {
var lightGray = '#fafafa'; return;
var red = '#ce1126'; }
var lightRed = '#fccfcf'; // $FlowFixMe
var yellow = '#fbf5b4'; var error = e.error;
function getHead() { if (error instanceof Error) {
return document.head || document.getElementsByTagName('head')[0]; 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 unregisterUnhandledError(target) {
function injectCss(css) { if (boundErrorHandler === null) {
var head = getHead(); return;
var style = document.createElement('style'); }
target.removeEventListener('error', boundErrorHandler);
boundErrorHandler = null;
}
style.type = 'text/css'; var boundRejectionHandler = null;
if (style.styleSheet) {
style.styleSheet.cssText = css; 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 { } 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); function getErrorRecord(ref) {
injectedCss.push(style); return recorded[ref];
} }
var css = [ function drain() {
'.cra-container {', // $FlowFixMe
' padding-right: 15px;', var keys = Object.keys(recorded);
' padding-left: 15px;', for (var index = 0; index < keys.length; ++index) {
' margin-right: auto;', delete recorded[keys[index]];
' 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');
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', position: 'fixed',
'box-sizing': 'border-box',
top: '1em', top: '1em',
left: '1em', left: '1em',
bottom: '1em', bottom: '1em',
...@@ -81,16 +224,19 @@ var overlayStyle = { ...@@ -81,16 +224,19 @@ var overlayStyle = {
width: 'calc(100% - 2em)', width: 'calc(100% - 2em)',
height: 'calc(100% - 2em)', height: 'calc(100% - 2em)',
'border-radius': '3px', 'border-radius': '3px',
'background-color': lightGray, 'box-shadow': '0 0 6px 0 rgba(0, 0, 0, 0.5)',
padding: '4rem',
'z-index': 1337, 'z-index': 1337,
};
var overlayStyle = {
'box-sizing': 'border-box',
padding: '4rem',
'font-family': 'Consolas, Menlo, monospace', 'font-family': 'Consolas, Menlo, monospace',
color: black, color: black,
'white-space': 'pre-wrap', 'white-space': 'pre-wrap',
overflow: 'auto', overflow: 'auto',
'overflow-x': 'hidden', 'overflow-x': 'hidden',
'word-break': 'break-all', 'word-break': 'break-word',
'box-shadow': '0 0 6px 0 rgba(0, 0, 0, 0.5)',
'line-height': 1.5, 'line-height': 1.5,
}; };
...@@ -220,153 +366,163 @@ var footerStyle = { ...@@ -220,153 +366,163 @@ var footerStyle = {
color: darkGray, 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) { function applyStyles(element, styles) {
element.setAttribute('style', ''); 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) { for (var key in styles) {
if (!styles.hasOwnProperty(key)) continue; if (!styles.hasOwnProperty(key)) {
var val = styles[key]; continue;
if (typeof val === 'function') val = val(); }
element.style[key] = val.toString(); // $FlowFixMe
element.style[key] = styles[key];
} }
} }
var overlayReference = null; function createHint(document, hint) {
var additionalReference = null; var span = document.createElement('span');
var capturedErrors = []; span.appendChild(document.createTextNode(hint));
var viewIndex = -1; applyStyles(span, hintStyle);
var frameSettings = []; return span;
}
function consumeEvent(e) { function createClose(document, callback) {
e.preventDefault(); var hints = document.createElement('div');
e.target.blur(); applyStyles(hints, hintsStyle);
var close = createHint(document, '×');
close.addEventListener('click', function() {
return callback();
});
applyStyles(close, closeButtonStyle);
hints.appendChild(close);
return hints;
} }
function accessify(node) { function enableTabClick(node) {
node.setAttribute('tabindex', 0); node.setAttribute('tabindex', '0');
node.addEventListener('keydown', function(e) { node.addEventListener('keydown', function(e) {
var key = e.key, which = e.which, keyCode = e.keyCode; var key = e.key, which = e.which, keyCode = e.keyCode;
if (key === 'Enter' || which === 13 || keyCode === 13) { if (key === 'Enter' || which === 13 || keyCode === 13) {
e.preventDefault(); 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) { function removeNextBr(parent, component) {
while (component != null && component.tagName.toLowerCase() !== 'br') { while (component != null && component.tagName.toLowerCase() !== 'br') {
component = component.nextSibling; component = component.nextElementSibling;
} }
if (component != null) { if (component != null) {
parent.removeChild(component); parent.removeChild(component);
} }
} }
function absolutifyCode(component) { function absolutifyCaret(component) {
var ccn = component.childNodes; var ccn = component.childNodes;
for (var index = 0; index < ccn.length; ++index) { for (var index = 0; index < ccn.length; ++index) {
var c = ccn[index]; var c = ccn[index];
if (c.tagName.toLowerCase() !== 'span') continue; // $FlowFixMe
var text = c.innerText.replace(/\s/g, ''); if (c.tagName.toLowerCase() !== 'span') {
if (text !== '|^') continue; continue;
}
var _text = c.innerText;
if (_text == null) {
continue;
}
var text = _text.replace(/\s/g, '');
if (text !== '|^') {
continue;
}
// $FlowFixMe
c.style.position = 'absolute'; c.style.position = 'absolute';
// $FlowFixMe
removeNextBr(component, c); removeNextBr(component, c);
} }
} }
function sourceCodePre(sourceLines, lineNum, columnNum) { function createCode(document, sourceLines, lineNum, columnNum, contextSize) {
var main = arguments.length > 3 && arguments[3] !== undefined var main = arguments.length > 5 && arguments[5] !== undefined
? arguments[3] ? arguments[5]
: false; : false;
var sourceCode = []; var sourceCode = [];
var whiteSpace = Infinity; var whiteSpace = Infinity;
sourceLines.forEach(function(_ref2) { sourceLines.forEach(function(e) {
var text = _ref2.text; var text = e.content;
var m = text.match(/^\s*/); var m = text.match(/^\s*/);
if (text === '') return; if (text === '') {
return;
}
if (m && m[0]) { if (m && m[0]) {
whiteSpace = Math.min(whiteSpace, m[0].length); whiteSpace = Math.min(whiteSpace, m[0].length);
} else { } else {
whiteSpace = 0; whiteSpace = 0;
} }
}); });
sourceLines.forEach(function(_ref3) { sourceLines.forEach(function(e) {
var text = _ref3.text, line = _ref3.line; 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[line - 1] = text;
}); });
sourceCode = sourceCode.join('\n');
var ansiHighlight = codeFrame( var ansiHighlight = codeFrame(
sourceCode, sourceCode.join('\n'),
lineNum, lineNum,
columnNum - (isFinite(whiteSpace) ? whiteSpace : 0), columnNum - (isFinite(whiteSpace) ? whiteSpace : 0),
{ {
forceColor: true, forceColor: true,
linesAbove: CONTEXT_SIZE, linesAbove: contextSize,
linesBelow: CONTEXT_SIZE, linesBelow: contextSize,
} }
); );
var htmlHighlight = ansiHTML(ansiHighlight); var htmlHighlight = ansiHTML(ansiHighlight);
var code = document.createElement('code'); var code = document.createElement('code');
code.innerHTML = htmlHighlight; code.innerHTML = htmlHighlight;
absolutifyCode(code); absolutifyCaret(code);
applyStyles(code, codeStyle); applyStyles(code, codeStyle);
var ccn = code.childNodes; 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 node = ccn[index];
var breakOut = false;
var ccn2 = node.childNodes; var ccn2 = node.childNodes;
for (var index2 = 0; index2 < ccn2.length; ++index2) { for (var index2 = 0; index2 < ccn2.length; ++index2) {
var lineNode = ccn2[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); applyStyles(node, main ? primaryErrorStyle : secondaryErrorStyle);
breakOut = true; break oLoop;
} }
if (breakOut) break;
} }
var pre = document.createElement('pre'); var pre = document.createElement('pre');
applyStyles(pre, preStyle); applyStyles(pre, preStyle);
...@@ -374,66 +530,17 @@ function sourceCodePre(sourceLines, lineNum, columnNum) { ...@@ -374,66 +530,17 @@ function sourceCodePre(sourceLines, lineNum, columnNum) {
return pre; return pre;
} }
function createHint(hint) { function isInternalFile(url, sourceFileName) {
var span = document.createElement('span'); return url.indexOf('/~/') !== -1 ||
span.appendChild(document.createTextNode(hint)); url.indexOf('/node_modules/') !== -1 ||
applyStyles(span, hintStyle); url.trim().indexOf(' ') !== -1 ||
return span; sourceFileName == null ||
} sourceFileName.length === 0;
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 getGroupToggle(omitsCount, omitBundle) { function getGroupToggle(document, omitsCount, omitBundle) {
var omittedFrames = document.createElement('div'); var omittedFrames = document.createElement('div');
accessify(omittedFrames); enableTabClick(omittedFrames);
var text1 = document.createTextNode( var text1 = document.createTextNode(
'\u25B6 ' + omitsCount + ' stack frames were collapsed.' '\u25B6 ' + omitsCount + ' stack frames were collapsed.'
); );
...@@ -461,15 +568,23 @@ function getGroupToggle(omitsCount, omitBundle) { ...@@ -461,15 +568,23 @@ function getGroupToggle(omitsCount, omitBundle) {
return omittedFrames; return omittedFrames;
} }
function insertBeforeBundle(parent, omitsCount, omitBundle, actionElement) { function insertBeforeBundle(
document,
parent,
omitsCount,
omitBundle,
actionElement
) {
var children = document.getElementsByName('bundle-' + omitBundle); var children = document.getElementsByName('bundle-' + omitBundle);
if (children.length < 1) return; if (children.length < 1) {
return;
}
var first = children[0]; var first = children[0];
while (first.parentNode !== parent) { while (first != null && first.parentNode !== parent) {
first = first.parentNode; first = first.parentNode;
} }
var div = document.createElement('div'); var div = document.createElement('div');
accessify(div); enableTabClick(div);
div.setAttribute('name', 'bundle-' + omitBundle); div.setAttribute('name', 'bundle-' + omitBundle);
var text = document.createTextNode( var text = document.createTextNode(
'\u25BC ' + omitsCount + ' stack frames were expanded.' '\u25BC ' + omitsCount + ' stack frames were expanded.'
...@@ -484,9 +599,48 @@ function insertBeforeBundle(parent, omitsCount, omitBundle, actionElement) { ...@@ -484,9 +599,48 @@ function insertBeforeBundle(parent, omitsCount, omitBundle, actionElement) {
parent.insertBefore(div, first); 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, frameSetting,
frame, frame,
contextSize,
critical, critical,
omits, omits,
omitBundle, omitBundle,
...@@ -498,19 +652,23 @@ function traceFrame( ...@@ -498,19 +652,23 @@ function traceFrame(
fileName = frame.fileName, fileName = frame.fileName,
lineNumber = frame.lineNumber, lineNumber = frame.lineNumber,
columnNumber = frame.columnNumber, columnNumber = frame.columnNumber,
scriptLines = frame.scriptLines, scriptLines = frame._scriptCode,
sourceFileName = frame.sourceFileName, sourceFileName = frame._originalFileName,
sourceLineNumber = frame.sourceLineNumber, sourceLineNumber = frame._originalLineNumber,
sourceColumnNumber = frame.sourceColumnNumber, sourceColumnNumber = frame._originalColumnNumber,
sourceLines = frame.sourceLines; sourceLines = frame._originalScriptCode;
var url = void 0; var url = void 0;
if (!compiled && sourceFileName) { if (!compiled && sourceFileName) {
url = sourceFileName + ':' + sourceLineNumber; url = sourceFileName + ':' + sourceLineNumber;
if (sourceColumnNumber) url += ':' + sourceColumnNumber; if (sourceColumnNumber) {
url += ':' + sourceColumnNumber;
}
} else { } else {
url = fileName + ':' + lineNumber; url = fileName + ':' + lineNumber;
if (columnNumber) url += ':' + columnNumber; if (columnNumber) {
url += ':' + columnNumber;
}
} }
var needsHidden = false; var needsHidden = false;
...@@ -522,19 +680,17 @@ function traceFrame( ...@@ -522,19 +680,17 @@ function traceFrame(
var collapseElement = null; var collapseElement = null;
if (!internalUrl || lastElement) { if (!internalUrl || lastElement) {
if (omits.value > 0) { if (omits.value > 0) {
var omittedFrames = getGroupToggle(omits.value, omitBundle); var capV = omits.value;
setTimeout( var omittedFrames = getGroupToggle(document, capV, omitBundle);
(function() { window.requestAnimationFrame(function() {
insertBeforeBundle.apply(undefined, arguments); insertBeforeBundle(
}).bind( document,
undefined,
parentContainer, parentContainer,
omits.value, capV,
omitBundle, omitBundle,
omittedFrames omittedFrames
), );
1 });
);
if (lastElement && internalUrl) { if (lastElement && internalUrl) {
collapseElement = omittedFrames; collapseElement = omittedFrames;
} else { } else {
...@@ -545,7 +701,7 @@ function traceFrame( ...@@ -545,7 +701,7 @@ function traceFrame(
omits.value = 0; omits.value = 0;
} }
var elem = frameDiv(functionName, url, internalUrl); var elem = frameDiv(document, functionName, url, internalUrl);
if (needsHidden) { if (needsHidden) {
applyStyles(elem, hiddenStyle); applyStyles(elem, hiddenStyle);
elem.setAttribute('name', 'bundle-' + omitBundle); elem.setAttribute('name', 'bundle-' + omitBundle);
...@@ -555,15 +711,24 @@ function traceFrame( ...@@ -555,15 +711,24 @@ function traceFrame(
if (!internalUrl) { if (!internalUrl) {
if (compiled && scriptLines.length !== 0) { if (compiled && scriptLines.length !== 0) {
elem.appendChild( elem.appendChild(
sourceCodePre(scriptLines, lineNumber, columnNumber, critical) createCode(
document,
scriptLines,
lineNumber,
columnNumber,
contextSize,
critical
)
); );
hasSource = true; hasSource = true;
} else if (!compiled && sourceLines.length !== 0) { } else if (!compiled && sourceLines.length !== 0) {
elem.appendChild( elem.appendChild(
sourceCodePre( createCode(
document,
sourceLines, sourceLines,
sourceLineNumber, sourceLineNumber,
sourceColumnNumber, sourceColumnNumber,
contextSize,
critical critical
) )
); );
...@@ -574,9 +739,18 @@ function traceFrame( ...@@ -574,9 +739,18 @@ function traceFrame(
return { elem: elem, hasSource: hasSource, collapseElement: collapseElement }; return { elem: elem, hasSource: hasSource, collapseElement: collapseElement };
} }
function lazyFrame(parent, factory, lIndex) { function createFrameWrapper(
document,
parent,
factory,
lIndex,
frameSettings,
contextSize
) {
var fac = factory(); var fac = factory();
if (fac == null) return; if (fac == null) {
return;
}
var hasSource = fac.hasSource, var hasSource = fac.hasSource,
elem = fac.elem, elem = fac.elem,
collapseElement = fac.collapseElement; collapseElement = fac.collapseElement;
...@@ -585,27 +759,34 @@ function lazyFrame(parent, factory, lIndex) { ...@@ -585,27 +759,34 @@ function lazyFrame(parent, factory, lIndex) {
elemWrapper.appendChild(elem); elemWrapper.appendChild(elem);
if (hasSource) { if (hasSource) {
(function() { var compiledDiv = document.createElement('div');
var compiledDiv = document.createElement('div'); enableTabClick(compiledDiv);
accessify(compiledDiv); applyStyles(compiledDiv, toggleStyle);
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 next = lazyFrame(parent, factory, lIndex); var o = frameSettings[lIndex];
if (next != null) { var compiledText = document.createTextNode(
parent.insertBefore(next, elemWrapper); 'View ' + (o && o.compiled ? 'source' : 'compiled')
parent.removeChild(elemWrapper); );
} compiledDiv.addEventListener('click', function() {
}); if (o) {
compiledDiv.appendChild(compiledText); o.compiled = !o.compiled;
elemWrapper.appendChild(compiledDiv); }
})();
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) { if (collapseElement != null) {
...@@ -615,7 +796,12 @@ function lazyFrame(parent, factory, lIndex) { ...@@ -615,7 +796,12 @@ function lazyFrame(parent, factory, lIndex) {
return elemWrapper; 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'); var trace = document.createElement('div');
applyStyles(trace, traceStyle); applyStyles(trace, traceStyle);
...@@ -624,21 +810,28 @@ function traceDiv(resolvedFrames) { ...@@ -624,21 +810,28 @@ function traceDiv(resolvedFrames) {
var omits = { value: 0, bundle: 1 }; var omits = { value: 0, bundle: 1 };
resolvedFrames.forEach(function(frame) { resolvedFrames.forEach(function(frame) {
var lIndex = index++; var lIndex = index++;
var elem = lazyFrame( var elem = createFrameWrapper(
document,
trace, trace,
traceFrame.bind( createFrame.bind(
undefined, undefined,
document,
frameSettings[lIndex], frameSettings[lIndex],
frame, frame,
contextSize,
critical, critical,
omits, omits,
omits.bundle, omits.bundle,
trace, trace,
index === resolvedFrames.length index === resolvedFrames.length
), ),
lIndex lIndex,
frameSettings,
contextSize
); );
if (elem == null) return; if (elem == null) {
return;
}
critical = false; critical = false;
trace.appendChild(elem); trace.appendChild(elem);
}); });
...@@ -648,7 +841,7 @@ function traceDiv(resolvedFrames) { ...@@ -648,7 +841,7 @@ function traceDiv(resolvedFrames) {
return trace; return trace;
} }
function footer() { function createFooter(document) {
var div = document.createElement('div'); var div = document.createElement('div');
applyStyles(div, footerStyle); applyStyles(div, footerStyle);
div.appendChild( div.appendChild(
...@@ -665,19 +858,74 @@ function footer() { ...@@ -665,19 +858,74 @@ function footer() {
return div; return div;
} }
function render(error, name, message, resolvedFrames) { function consumeEvent(e) {
dispose(); e.preventDefault();
if (typeof e.target.blur === 'function') {
e.target.blur();
}
}
frameSettings = resolvedFrames.map(function() { function updateAdditional(
return { compiled: false }; 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 // Create overlay
var overlay = document.createElement('div'); var overlay = document.createElement('div');
applyStyles(overlay, overlayStyle); applyStyles(overlay, overlayStyle);
overlay.appendChild(hintsDiv()); overlay.appendChild(createClose(document, closeCallback));
// Create container // Create container
var container = document.createElement('div'); var container = document.createElement('div');
...@@ -685,10 +933,16 @@ function render(error, name, message, resolvedFrames) { ...@@ -685,10 +933,16 @@ function render(error, name, message, resolvedFrames) {
overlay.appendChild(container); overlay.appendChild(container);
// Create additional // Create additional
additionalReference = document.createElement('div'); var additional = document.createElement('div');
applyStyles(additionalReference, additionalStyle); applyStyles(additional, additionalStyle);
container.appendChild(additionalReference); container.appendChild(additional);
renderAdditional(); updateAdditional(
document,
additional,
currentError,
totalErrors,
switchCallback
);
// Create header // Create header
var header = document.createElement('div'); var header = document.createElement('div');
...@@ -701,235 +955,216 @@ function render(error, name, message, resolvedFrames) { ...@@ -701,235 +955,216 @@ function render(error, name, message, resolvedFrames) {
container.appendChild(header); container.appendChild(header);
// Create trace // Create trace
container.appendChild(traceDiv(resolvedFrames)); container.appendChild(
createFrames(document, frames, frameSettings, contextSize)
);
// Show message // Show message
container.appendChild(footer()); container.appendChild(createFooter(document));
// Mount return {
document.body.appendChild((overlayReference = overlay)); overlay: overlay,
additional: additional,
};
} }
function dispose() { var CONTEXT_SIZE = 3;
if (overlayReference === null) return; var iframeReference = null;
document.body.removeChild(overlayReference); var additionalReference = null;
overlayReference = null; var errorReferences = [];
var head = getHead(); var currReferenceIndex = -1;
injectedCss.forEach(function(node) {
head.removeChild(node);
});
injectedCss = [];
}
function unmount() { var css = [
dispose(); '.cra-container {',
capturedErrors = []; ' padding-right: 15px;',
viewIndex = -1; ' 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) { function render(name, message, resolvedFrames) {
return url.indexOf('/~/') !== -1 || disposeCurrentView();
url.trim().indexOf(' ') !== -1 ||
!sourceFileName; 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) { function renderErrorByIndex(index) {
viewIndex = index; currReferenceIndex = index;
var _capturedErrors$index = capturedErrors[index],
error = _capturedErrors$index.error, var _getErrorRecord = getErrorRecord(errorReferences[index]),
unhandledRejection = _capturedErrors$index.unhandledRejection, error = _getErrorRecord.error,
resolvedFrames = _capturedErrors$index.resolvedFrames; unhandledRejection = _getErrorRecord.unhandledRejection,
enhancedFrames = _getErrorRecord.enhancedFrames;
if (unhandledRejection) { if (unhandledRejection) {
render( render(
error,
'Unhandled Rejection (' + error.name + ')', 'Unhandled Rejection (' + error.name + ')',
error.message, error.message,
resolvedFrames enhancedFrames
); );
} else { } 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) { function crash(error) {
var unhandledRejection = arguments.length > 1 && arguments[1] !== undefined var unhandledRejection = arguments.length > 1 && arguments[1] !== undefined
? arguments[1] ? arguments[1]
: false; : false;
var sourceOverrides = arguments.length > 2 && arguments[2] !== undefined
? arguments[2]
: [];
if (module.hot) module.hot.decline(); if (module.hot && typeof module.hot.decline === 'function') {
module.hot.decline();
StackTraceResolve(error, CONTEXT_SIZE) }
.then(function(resolvedFrames) { consume(error, unhandledRejection, CONTEXT_SIZE)
resolvedFrames = resolvedFrames.filter(function(_ref) { .then(function(ref) {
var functionName = _ref.functionName; errorReferences.push(ref);
return functionName.indexOf('__cra_proxy_console__') === -1; if (iframeReference !== null && additionalReference !== null) {
}); updateAdditional(
var overrideCount = sourceOverrides.length, iframeReference.contentDocument,
frameCount = resolvedFrames.length; additionalReference,
var frameIndex = 0; currReferenceIndex + 1,
for ( errorReferences.length,
var overrideIndex = 0; function(offset) {
overrideIndex < overrideCount; switchError(offset);
++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;
} }
);
} else {
if (errorReferences.length !== 1) {
throw new Error('Something is *really* wrong.');
} }
if (shouldContinue) continue; renderErrorByIndex((currReferenceIndex = 0));
break;
}
capturedErrors.push({
error: error,
unhandledRejection: unhandledRejection,
resolvedFrames: resolvedFrames,
});
if (overlayReference !== null)
renderAdditional();
else {
renderError((viewIndex = 0));
} }
}) })
.catch(function(e) { .catch(function(e) {
// This is another fail case (unlikely to happen) console.log('Could not consume error:', e);
// 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).',
[]
);
}); });
} }
function switchError(offset) { function shortcutHandler(type) {
try { switch (type) {
var nextView = viewIndex + offset; case SHORTCUT_ESCAPE: {
if (nextView < 0 || nextView >= capturedErrors.length) return; unmount();
renderError(nextView); break;
} catch (e) { }
console.log('Red box renderer error:', e); case SHORTCUT_LEFT: {
unmount(); switchError(-1);
render( break;
null, }
'Error', case SHORTCUT_RIGHT: {
'There is an error with red box. *Please* report this (see console).', switchError(1);
[] break;
);
}
}
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);
} }
} 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); function inject() {
registerUnhandledError(window, function(error) {
try { return crash(error);
Error.stackTraceLimit = 50; });
} catch (e) { registerUnhandledRejection(window, function(error) {
// Browser may not support this, we don't care. return crash(error, true);
} });
registerShortcuts(window, shortcutHandler);
// eslint-disable-next-line registerStackTraceLimit();
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);
};
};
// 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() { module.hot.dispose(function() {
unmount(); uninject();
window.removeEventListener('unhandledrejection', promiseHandler);
window.removeEventListener('keydown', escapeHandler);
}); });
} }
...@@ -36,7 +36,9 @@ ...@@ -36,7 +36,9 @@
"opn": "4.0.2", "opn": "4.0.2",
"recursive-readdir": "2.1.1", "recursive-readdir": "2.1.1",
"sockjs-client": "1.1.2", "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" "strip-ansi": "3.0.1"
} }
} }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment