mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
b2bafa06fa
Summary: This diff adds support for thrown exceptions to redboxes, and hides the native redbox when we show an error in LogBox. Changelog: [Internal] Reviewed By: cpojer Differential Revision: D18212064 fbshipit-source-id: 92031d554968bcb079f81568673ae85697c8f5ad
158 lines
4.2 KiB
JavaScript
158 lines
4.2 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import UTFSequence from '../../UTFSequence';
|
|
import stringifySafe from '../../Utilities/stringifySafe';
|
|
import type {Stack} from './LogBoxSymbolication';
|
|
|
|
export type Category = string;
|
|
export type Message = $ReadOnly<{|
|
|
content: string,
|
|
substitutions: $ReadOnlyArray<
|
|
$ReadOnly<{|
|
|
length: number,
|
|
offset: number,
|
|
|}>,
|
|
>,
|
|
|}>;
|
|
|
|
export type ComponentStack = $ReadOnlyArray<
|
|
$ReadOnly<{|
|
|
component: string,
|
|
location: string,
|
|
|}>,
|
|
>;
|
|
|
|
const SUBSTITUTION = UTFSequence.BOM + '%s';
|
|
|
|
export function parseCategory(
|
|
args: $ReadOnlyArray<mixed>,
|
|
): $ReadOnly<{|
|
|
category: Category,
|
|
message: Message,
|
|
|}> {
|
|
const categoryParts = [];
|
|
const contentParts = [];
|
|
const substitutionOffsets = [];
|
|
|
|
const remaining = [...args];
|
|
if (typeof remaining[0] === 'string') {
|
|
const formatString = String(remaining.shift());
|
|
const formatStringParts = formatString.split('%s');
|
|
const substitutionCount = formatStringParts.length - 1;
|
|
const substitutions = remaining.splice(0, substitutionCount);
|
|
|
|
let categoryString = '';
|
|
let contentString = '';
|
|
|
|
let substitutionIndex = 0;
|
|
for (const formatStringPart of formatStringParts) {
|
|
categoryString += formatStringPart;
|
|
contentString += formatStringPart;
|
|
|
|
if (substitutionIndex < substitutionCount) {
|
|
if (substitutionIndex < substitutions.length) {
|
|
// Don't stringify a string type.
|
|
// It adds quotation mark wrappers around the string,
|
|
// which causes the LogBox to look odd.
|
|
const substitution =
|
|
typeof substitutions[substitutionIndex] === 'string'
|
|
? substitutions[substitutionIndex]
|
|
: stringifySafe(substitutions[substitutionIndex]);
|
|
substitutionOffsets.push({
|
|
length: substitution.length,
|
|
offset: contentString.length,
|
|
});
|
|
|
|
categoryString += SUBSTITUTION;
|
|
contentString += substitution;
|
|
} else {
|
|
substitutionOffsets.push({
|
|
length: 2,
|
|
offset: contentString.length,
|
|
});
|
|
|
|
categoryString += '%s';
|
|
contentString += '%s';
|
|
}
|
|
|
|
substitutionIndex++;
|
|
}
|
|
}
|
|
|
|
categoryParts.push(categoryString);
|
|
contentParts.push(contentString);
|
|
}
|
|
|
|
const remainingArgs = remaining.map(arg => {
|
|
// Don't stringify a string type.
|
|
// It adds quotation mark wrappers around the string,
|
|
// which causes the LogBox to look odd.
|
|
return typeof arg === 'string' ? arg : stringifySafe(arg);
|
|
});
|
|
categoryParts.push(...remainingArgs);
|
|
contentParts.push(...remainingArgs);
|
|
|
|
return {
|
|
category: categoryParts.join(' '),
|
|
message: {
|
|
content: contentParts.join(' '),
|
|
substitutions: substitutionOffsets,
|
|
},
|
|
};
|
|
}
|
|
export function parseComponentStack(message: string): ComponentStack {
|
|
return message
|
|
.split(/\n {4}in /g)
|
|
.map(s => {
|
|
if (!s) {
|
|
return null;
|
|
}
|
|
let [component, location] = s.split(/ \(at /);
|
|
if (!location) {
|
|
[component, location] = s.split(/ \(/);
|
|
}
|
|
return {component, location: location && location.replace(')', '')};
|
|
})
|
|
.filter(Boolean);
|
|
}
|
|
|
|
export function parseLogBoxLog(
|
|
args: $ReadOnlyArray<mixed>,
|
|
): {|
|
|
componentStack: ComponentStack,
|
|
category: Category,
|
|
message: Message,
|
|
|} {
|
|
// This detects a very narrow case of a simple log string,
|
|
// with a component stack appended by React DevTools.
|
|
// In this case, we extract the component stack,
|
|
// because LogBox formats those pleasantly.
|
|
// If there are other substitutions or formatting,
|
|
// this could potentially corrupt the data, but there
|
|
// are currently no known cases of that happening.
|
|
let componentStack = [];
|
|
let argsWithoutComponentStack = [];
|
|
for (const arg of args) {
|
|
if (typeof arg === 'string' && /^\n {4}in/.exec(arg)) {
|
|
componentStack = parseComponentStack(arg);
|
|
} else {
|
|
argsWithoutComponentStack.push(arg);
|
|
}
|
|
}
|
|
|
|
return {
|
|
...parseCategory(argsWithoutComponentStack),
|
|
componentStack,
|
|
};
|
|
}
|