-
-
Notifications
You must be signed in to change notification settings - Fork 27k
Improve unmapper file heuristic, add limited warning support, and ignore internal errors #2128
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
Changes from 16 commits
afba3fc
47cd783
cf897f0
a150609
8dfb007
4bae90d
ab81262
79af0fc
ec12b8c
fb7411b
cd9c43f
83e6324
ede4f9b
8997f58
19eadc2
43a97e3
52a39a4
4d9f647
d7f81e7
2d4fdad
4cb6be3
11fc0f0
9da83b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,42 @@ | ||
/* @flow */ | ||
type ConsoleProxyCallback = (message: string) => void; | ||
|
||
type ReactFrame = { | ||
fileName: string | null, | ||
lineNumber: number | null, | ||
functionName: string | null, | ||
}; | ||
const ReactFrameStack: Array<ReactFrame[]> = []; | ||
|
||
export type { ReactFrame }; | ||
|
||
const registerReactStack = () => { | ||
// $FlowFixMe | ||
console.stack = frames => ReactFrameStack.push(frames); | ||
// $FlowFixMe | ||
console.stackEnd = frames => ReactFrameStack.pop(); | ||
}; | ||
|
||
const unregisterReactStack = () => { | ||
// $FlowFixMe | ||
console.stack = undefined; | ||
// $FlowFixMe | ||
console.stackEnd = undefined; | ||
}; | ||
|
||
type ConsoleProxyCallback = ( | ||
message: string, | ||
frames: ReactFrame[] | void | ||
) => void; | ||
const permanentRegister = function proxyConsole( | ||
type: string, | ||
callback: ConsoleProxyCallback | ||
) { | ||
const orig = console[type]; | ||
console[type] = function __stack_frame_overlay_proxy_console__() { | ||
const message = [].slice.call(arguments).join(' '); | ||
callback(message); | ||
callback(message, ReactFrameStack[ReactFrameStack.length - 1]); | ||
return orig.apply(this, arguments); | ||
}; | ||
}; | ||
|
||
export { permanentRegister }; | ||
export { permanentRegister, registerReactStack, unregisterReactStack }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,19 @@ import { getSourceMap } from './getSourceMap'; | |
import { getLinesAround } from './getLinesAround'; | ||
import path from 'path'; | ||
|
||
function count(search: string, string: string): number { | ||
// Count starts at -1 becuse a do-while loop always runs at least once | ||
let count = -1, index = -1; | ||
do { | ||
// First call or the while case evaluated true, meaning we have to make | ||
// count 0 or we found a character | ||
++count; | ||
// Find the index of our search string, starting after the previous index | ||
index = string.indexOf(search, index + 1); | ||
} while (index !== -1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's better thanks! |
||
return count; | ||
} | ||
|
||
/** | ||
* Turns a set of mapped <code>StackFrame</code>s back into their generated code position and enhances them with code. | ||
* @param {string} fileUri The URI of the <code>bundle.js</code> file. | ||
|
@@ -39,28 +52,23 @@ async function unmap( | |
return frame; | ||
} | ||
const fN: string = fileName; | ||
const splitCache1: any = {}, splitCache2: any = {}, splitCache3: any = {}; | ||
const source = map | ||
.getSources() | ||
.map(s => s.replace(/[\\]+/g, '/')) | ||
.filter(s => { | ||
s = path.normalize(s); | ||
return s.indexOf(fN) === s.length - fN.length; | ||
}) | ||
.sort((a, b) => { | ||
let a2 = splitCache1[a] || (splitCache1[a] = a.split(path.sep)), | ||
b2 = splitCache1[b] || (splitCache1[b] = b.split(path.sep)); | ||
return Math.sign(a2.length - b2.length); | ||
}) | ||
.sort((a, b) => { | ||
let a2 = splitCache2[a] || (splitCache2[a] = a.split('node_modules')), | ||
b2 = splitCache2[b] || (splitCache2[b] = b.split('node_modules')); | ||
return Math.sign(a2.length - b2.length); | ||
.filter(p => { | ||
p = path.normalize(p); | ||
const i = p.lastIndexOf(fN); | ||
return i !== -1 && i === p.length - fN.length; | ||
}) | ||
.map(p => ({ | ||
token: p, | ||
seps: count(path.sep, path.normalize(p)), | ||
penalties: count('node_modules', p) + count('~', p), | ||
})) | ||
.sort((a, b) => { | ||
let a2 = splitCache3[a] || (splitCache3[a] = a.split('~')), | ||
b2 = splitCache3[b] || (splitCache3[b] = b.split('~')); | ||
return Math.sign(a2.length - b2.length); | ||
const s = Math.sign(a.seps - b.seps); | ||
if (s !== 0) return s; | ||
return Math.sign(a.penalties - b.penalties); | ||
}); | ||
if (source.length < 1 || lineNumber == null) { | ||
return new StackFrame( | ||
|
@@ -76,13 +84,14 @@ async function unmap( | |
null | ||
); | ||
} | ||
const sourceT = source[0].token; | ||
const { line, column } = map.getGeneratedPosition( | ||
source[0], | ||
sourceT, | ||
lineNumber, | ||
// $FlowFixMe | ||
columnNumber | ||
); | ||
const originalSource = map.getSource(source[0]); | ||
const originalSource = map.getSource(sourceT); | ||
return new StackFrame( | ||
functionName, | ||
fileUri, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// @flow | ||
import type { ReactFrame } from '../effects/proxyConsole'; | ||
|
||
// This is a list of information to remove from React warnings, it's not particularly useful to show. | ||
const removals = [/Check your code at (.*?:.*)[.]/]; | ||
|
||
function massage( | ||
warning: string, | ||
frames: ReactFrame[] | void | ||
): { message: string, stack: string } | null { | ||
if (!frames) { | ||
return null; | ||
} | ||
|
||
let message = warning; | ||
const nIndex = message.indexOf('\n'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will crash if warning is not a string. Technically user can call |
||
if (nIndex !== -1) message = message.substring(0, nIndex); | ||
|
||
for (const trim of removals) { | ||
message = message.replace(trim, ''); | ||
} | ||
|
||
let stack = ''; | ||
for (let index = 0; index < frames.length; ++index) { | ||
const { fileName, lineNumber } = frames[index]; | ||
if (fileName == null || lineNumber == null) continue; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code style: I'd like to reformat to always use |
||
let { functionName } = frames[index]; | ||
if (functionName == null && index === 0 && index + 1 < frames.length) { | ||
functionName = frames[index + 1].functionName; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is hacky, I should fix this on my side instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, you shouldn't need this anymore. I updated facebook/react#9679. |
||
if (functionName !== null) functionName = `(${functionName})`; | ||
} | ||
functionName = functionName || '(unknown function)'; | ||
|
||
stack += `in ${functionName} (at ${fileName}:${lineNumber})\n`; | ||
} | ||
return { message, stack }; | ||
} | ||
|
||
export { massage }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not quite what the browser does though. It also supports interpolations. For example
console.warn('hello %s', 'friend')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I'll just use the first argument.