18
export {default as formatWithStyles} from './formatWithStyles';
19
export {default as formatConsoleArguments} from './formatConsoleArguments';
20
21
+import {formatOwnerStackString} from '../shared/DevToolsOwnerStack';
22
+
23
// TODO: update this to the first React version that has a corresponding DevTools backend
24
const FIRST_DEVTOOLS_BACKEND_LOCKSTEP_VER = '999.9.9';
25
export function hasAssignedBackend(version?: string): boolean {
347
return parseSourceFromFirefoxStack(componentStack);
348
}
349
350
+let collectedLocation: Source | null = null;
351
+
352
+function collectStackTrace(
353
+ error: Error,
354
+ structuredStackTrace: CallSite[],
355
+): string {
356
+ let result: null | Source = null;
357
+ // Collect structured stack traces from the callsites.
358
+ // We mirror how V8 serializes stack frames and how we later parse them.
359
+ for (let i = 0; i < structuredStackTrace.length; i++) {
360
+ const callSite = structuredStackTrace[i];
361
+ if (callSite.getFunctionName() === 'react-stack-bottom-frame') {
362
+ // We pick the last frame that matches before the bottom frame since
363
+ // that will be immediately inside the component as opposed to some helper.
364
+ // If we don't find a bottom frame then we bail to string parsing.
365
+ collectedLocation = result;
366
+ // Skip everything after the bottom frame since it'll be internals.
367
+ break;
368
+ } else {
369
+ const sourceURL = callSite.getScriptNameOrSourceURL();
370
+ const line =
371
+ // $FlowFixMe[prop-missing]
372
+ typeof callSite.getEnclosingLineNumber === 'function'
373
+ ? (callSite: any).getEnclosingLineNumber()
374
+ : callSite.getLineNumber();
375
+ const col =
376
+ // $FlowFixMe[prop-missing]
377
+ typeof callSite.getEnclosingColumnNumber === 'function'
378
+ ? (callSite: any).getEnclosingColumnNumber()
379
+ : callSite.getLineNumber();
380
+ if (!sourceURL || !line || !col) {
381
+ // Skip eval etc. without source url. They don't have location.
382
+ continue;
383
+ }
384
+ result = {
385
+ sourceURL,
386
+ line: line,
387
+ column: col,
388
+ };
389
+ }
390
+ }
391
+ // At the same time we generate a string stack trace just in case someone
392
+ // else reads it.
393
+ const name = error.name || 'Error';
394
+ const message = error.message || '';
395
+ let stack = name + ': ' + message;
396
+ for (let i = 0; i < structuredStackTrace.length; i++) {
397
+ stack += '\n at ' + structuredStackTrace[i].toString();
398
+ }
399
+ return stack;
400
+}
401
+
402
+export function parseSourceFromOwnerStack(error: Error): Source | null {
403
+ // First attempt to collected the structured data using prepareStackTrace.
404
+ collectedLocation = null;
405
+ const previousPrepare = Error.prepareStackTrace;
406
+ Error.prepareStackTrace = collectStackTrace;
407
+ let stack;
408
+ try {
409
+ stack = error.stack;
410
+ } finally {
411
+ Error.prepareStackTrace = previousPrepare;
412
+ }
413
+ if (collectedLocation !== null) {
414
+ return collectedLocation;
415
+ }
416
+ // Fallback to parsing the string form.
417
+ const componentStack = formatOwnerStackString(stack);
418
+ return parseSourceFromComponentStack(componentStack);
419
+}
420
+
421
// 0.123456789 => 0.123
422
// Expects high-resolution timestamp in milliseconds, like from performance.now()
423
// Mainly used for optimizing the size of serialized profiling payload