Commit 8b451c38 authored by Dan Abramov's avatar Dan Abramov Committed by GitHub
Browse files

Don't collapse unintentional top-level errors (#2145)

* Don't collapse unintentional top-level errors

* Linkify internal stack frames too
parent ac7777d5
Showing with 135 additions and 51 deletions
+135 -51
...@@ -20,8 +20,7 @@ function createCode( ...@@ -20,8 +20,7 @@ function createCode(
columnNum: number | null, columnNum: number | null,
contextSize: number, contextSize: number,
main: boolean, main: boolean,
clickToOpenFileName: ?string, onSourceClick: ?Function
clickToOpenLineNumber: ?number
) { ) {
const sourceCode = []; const sourceCode = [];
let whiteSpace = Infinity; let whiteSpace = Infinity;
...@@ -86,15 +85,11 @@ function createCode( ...@@ -86,15 +85,11 @@ function createCode(
applyStyles(pre, preStyle); applyStyles(pre, preStyle);
pre.appendChild(code); pre.appendChild(code);
if (clickToOpenFileName) { if (typeof onSourceClick === 'function') {
let handler = onSourceClick;
pre.style.cursor = 'pointer'; pre.style.cursor = 'pointer';
pre.addEventListener('click', function() { pre.addEventListener('click', function() {
fetch( handler();
'/__open-stack-frame-in-editor?fileName=' +
window.encodeURIComponent(clickToOpenFileName) +
'&lineNumber=' +
window.encodeURIComponent(clickToOpenLineNumber || 1)
).then(() => {}, () => {});
}); });
} }
......
...@@ -79,7 +79,13 @@ function insertBeforeBundle( ...@@ -79,7 +79,13 @@ function insertBeforeBundle(
parent.insertBefore(div, first); parent.insertBefore(div, first);
} }
function frameDiv(document: Document, functionName, url, internalUrl) { function frameDiv(
document: Document,
functionName,
url,
internalUrl,
onSourceClick: ?Function
) {
const frame = document.createElement('div'); const frame = document.createElement('div');
const frameFunctionName = document.createElement('div'); const frameFunctionName = document.createElement('div');
...@@ -112,9 +118,69 @@ function frameDiv(document: Document, functionName, url, internalUrl) { ...@@ -112,9 +118,69 @@ function frameDiv(document: Document, functionName, url, internalUrl) {
frameLink.appendChild(frameAnchor); frameLink.appendChild(frameAnchor);
frame.appendChild(frameLink); frame.appendChild(frameLink);
if (typeof onSourceClick === 'function') {
let handler = onSourceClick;
frameAnchor.style.cursor = 'pointer';
frameAnchor.addEventListener('click', function() {
handler();
});
}
return frame; return frame;
} }
function isBultinErrorName(errorName: ?string) {
switch (errorName) {
case 'EvalError':
case 'InternalError':
case 'RangeError':
case 'ReferenceError':
case 'SyntaxError':
case 'TypeError':
case 'URIError':
return true;
default:
return false;
}
}
function getPrettyURL(
sourceFileName: ?string,
sourceLineNumber: ?number,
sourceColumnNumber: ?number,
fileName: ?string,
lineNumber: ?number,
columnNumber: ?number,
compiled: boolean
): string {
let prettyURL;
if (!compiled && sourceFileName && typeof sourceLineNumber === 'number') {
// Remove everything up to the first /src/ or /node_modules/
const trimMatch = /^[/|\\].*?[/|\\]((src|node_modules)[/|\\].*)/.exec(
sourceFileName
);
if (trimMatch && trimMatch[1]) {
prettyURL = trimMatch[1];
} else {
prettyURL = sourceFileName;
}
prettyURL += ':' + sourceLineNumber;
// Note: we intentionally skip 0's because they're produced by cheap Webpack maps
if (sourceColumnNumber) {
prettyURL += ':' + sourceColumnNumber;
}
} else if (fileName && typeof lineNumber === 'number') {
prettyURL = fileName + ':' + lineNumber;
// Note: we intentionally skip 0's because they're produced by cheap Webpack maps
if (columnNumber) {
prettyURL += ':' + columnNumber;
}
} else {
prettyURL = 'unknown';
}
return prettyURL;
}
function createFrame( function createFrame(
document: Document, document: Document,
frameSetting: FrameSetting, frameSetting: FrameSetting,
...@@ -124,7 +190,8 @@ function createFrame( ...@@ -124,7 +190,8 @@ function createFrame(
omits: OmitsObject, omits: OmitsObject,
omitBundle: number, omitBundle: number,
parentContainer: HTMLDivElement, parentContainer: HTMLDivElement,
lastElement: boolean lastElement: boolean,
errorName: ?string
) { ) {
const { compiled } = frameSetting; const { compiled } = frameSetting;
let { functionName, _originalFileName: sourceFileName } = frame; let { functionName, _originalFileName: sourceFileName } = frame;
...@@ -149,35 +216,33 @@ function createFrame( ...@@ -149,35 +216,33 @@ function createFrame(
functionName = '(anonymous function)'; functionName = '(anonymous function)';
} }
let url; const prettyURL = getPrettyURL(
if (!compiled && sourceFileName && sourceLineNumber) { sourceFileName,
// Remove everything up to the first /src/ sourceLineNumber,
const trimMatch = /^[/|\\].*?[/|\\](src[/|\\].*)/.exec(sourceFileName); sourceColumnNumber,
if (trimMatch && trimMatch[1]) { fileName,
sourceFileName = trimMatch[1]; lineNumber,
} columnNumber,
compiled
);
url = sourceFileName + ':' + sourceLineNumber; let needsHidden = false;
if (sourceColumnNumber) { const isInternalUrl = isInternalFile(sourceFileName, fileName);
url += ':' + sourceColumnNumber; const isThrownIntentionally = !isBultinErrorName(errorName);
} const shouldCollapse = isInternalUrl &&
} else if (fileName && lineNumber) { (isThrownIntentionally || omits.hasReachedAppCode);
url = fileName + ':' + lineNumber;
if (columnNumber) { if (!isInternalUrl) {
url += ':' + columnNumber; omits.hasReachedAppCode = true;
}
} else {
url = 'unknown';
} }
let needsHidden = false; if (shouldCollapse) {
const internalUrl = isInternalFile(url, sourceFileName);
if (internalUrl) {
++omits.value; ++omits.value;
needsHidden = true; needsHidden = true;
} }
let collapseElement = null; let collapseElement = null;
if (!internalUrl || lastElement) { if (!shouldCollapse || lastElement) {
if (omits.value > 0) { if (omits.value > 0) {
const capV = omits.value; const capV = omits.value;
const omittedFrames = getGroupToggle(document, capV, omitBundle); const omittedFrames = getGroupToggle(document, capV, omitBundle);
...@@ -190,7 +255,7 @@ function createFrame( ...@@ -190,7 +255,7 @@ function createFrame(
omittedFrames omittedFrames
); );
}); });
if (lastElement && internalUrl) { if (lastElement && shouldCollapse) {
collapseElement = omittedFrames; collapseElement = omittedFrames;
} else { } else {
parentContainer.appendChild(omittedFrames); parentContainer.appendChild(omittedFrames);
...@@ -200,14 +265,32 @@ function createFrame( ...@@ -200,14 +265,32 @@ function createFrame(
omits.value = 0; omits.value = 0;
} }
const elem = frameDiv(document, functionName, url, internalUrl); let onSourceClick = null;
if (sourceFileName) {
onSourceClick = () => {
fetch(
'/__open-stack-frame-in-editor?fileName=' +
window.encodeURIComponent(sourceFileName) +
'&lineNumber=' +
window.encodeURIComponent(sourceLineNumber || 1)
).then(() => {}, () => {});
};
}
const elem = frameDiv(
document,
functionName,
prettyURL,
shouldCollapse,
onSourceClick
);
if (needsHidden) { if (needsHidden) {
applyStyles(elem, hiddenStyle); applyStyles(elem, hiddenStyle);
elem.setAttribute('name', 'bundle-' + omitBundle); elem.setAttribute('name', 'bundle-' + omitBundle);
} }
let hasSource = false; let hasSource = false;
if (!internalUrl) { if (!shouldCollapse) {
if ( if (
compiled && scriptLines && scriptLines.length !== 0 && lineNumber != null compiled && scriptLines && scriptLines.length !== 0 && lineNumber != null
) { ) {
...@@ -219,8 +302,7 @@ function createFrame( ...@@ -219,8 +302,7 @@ function createFrame(
columnNumber, columnNumber,
contextSize, contextSize,
critical, critical,
frame._originalFileName, onSourceClick
frame._originalLineNumber
) )
); );
hasSource = true; hasSource = true;
...@@ -238,8 +320,7 @@ function createFrame( ...@@ -238,8 +320,7 @@ function createFrame(
sourceColumnNumber, sourceColumnNumber,
contextSize, contextSize,
critical, critical,
frame._originalFileName, onSourceClick
frame._originalLineNumber
) )
); );
hasSource = true; hasSource = true;
......
...@@ -5,7 +5,11 @@ import { traceStyle, toggleStyle } from '../styles'; ...@@ -5,7 +5,11 @@ import { traceStyle, toggleStyle } from '../styles';
import { enableTabClick } from '../utils/dom/enableTabClick'; import { enableTabClick } from '../utils/dom/enableTabClick';
import { createFrame } from './frame'; import { createFrame } from './frame';
type OmitsObject = { value: number, bundle: number }; type OmitsObject = {
value: number,
bundle: number,
hasReachedAppCode: boolean,
};
type FrameSetting = { compiled: boolean }; type FrameSetting = { compiled: boolean };
export type { OmitsObject, FrameSetting }; export type { OmitsObject, FrameSetting };
...@@ -68,7 +72,8 @@ function createFrames( ...@@ -68,7 +72,8 @@ function createFrames(
document: Document, document: Document,
resolvedFrames: StackFrame[], resolvedFrames: StackFrame[],
frameSettings: FrameSetting[], frameSettings: FrameSetting[],
contextSize: number contextSize: number,
errorName: ?string
) { ) {
if (resolvedFrames.length !== frameSettings.length) { if (resolvedFrames.length !== frameSettings.length) {
throw new Error( throw new Error(
...@@ -80,7 +85,7 @@ function createFrames( ...@@ -80,7 +85,7 @@ function createFrames(
let index = 0; let index = 0;
let critical = true; let critical = true;
const omits: OmitsObject = { value: 0, bundle: 1 }; const omits: OmitsObject = { value: 0, bundle: 1, hasReachedAppCode: false };
resolvedFrames.forEach(function(frame) { resolvedFrames.forEach(function(frame) {
const lIndex = index++; const lIndex = index++;
const elem = createFrameWrapper( const elem = createFrameWrapper(
...@@ -96,7 +101,8 @@ function createFrames( ...@@ -96,7 +101,8 @@ function createFrames(
omits, omits,
omits.bundle, omits.bundle,
trace, trace,
index === resolvedFrames.length index === resolvedFrames.length,
errorName
), ),
lIndex, lIndex,
frameSettings, frameSettings,
......
...@@ -73,7 +73,7 @@ function createOverlay( ...@@ -73,7 +73,7 @@ function createOverlay(
// Create trace // Create trace
container.appendChild( container.appendChild(
createFrames(document, frames, frameSettings, contextSize) createFrames(document, frames, frameSettings, contextSize, name)
); );
// Show message // Show message
......
/* @flow */ /* @flow */
function isInternalFile(url: string, sourceFileName: string | null | void) { function isInternalFile(sourceFileName: ?string, fileName: ?string) {
return url.indexOf('/~/') !== -1 || return sourceFileName == null ||
url.indexOf('/node_modules/') !== -1 || sourceFileName === '' ||
url.trim().indexOf(' ') !== -1 || sourceFileName.indexOf('/~/') !== -1 ||
sourceFileName == null || sourceFileName.indexOf('/node_modules/') !== -1 ||
sourceFileName.length === 0; sourceFileName.trim().indexOf(' ') !== -1 ||
fileName == null ||
fileName === '';
} }
export { isInternalFile }; export { isInternalFile };
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