diff --git a/Libraries/Core/Devtools/symbolicateStackTrace.js b/Libraries/Core/Devtools/symbolicateStackTrace.js
index 26d00d72d76..ef128990867 100644
--- a/Libraries/Core/Devtools/symbolicateStackTrace.js
+++ b/Libraries/Core/Devtools/symbolicateStackTrace.js
@@ -21,7 +21,7 @@ import type {StackFrame} from '../NativeExceptionsManager';
export type CodeFrame = $ReadOnly<{|
content: string,
- location: {
+ location: ?{
row: number,
column: number,
...
diff --git a/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js b/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js
index 889ec9ef79e..cc114c222b2 100644
--- a/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js
+++ b/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js
@@ -177,7 +177,32 @@ describe('parseLogBoxLog', () => {
});
});
- it('parses a syntax error', () => {
+ it('parses a transform error as a fatal', () => {
+ const error = {
+ message: 'TransformError failed to transform file.',
+ originalMessage: 'TransformError failed to transform file.',
+ name: '',
+ isComponentError: false,
+ componentStack: '',
+ stack: [],
+ id: 0,
+ isFatal: true,
+ };
+
+ expect(parseLogBoxException(error)).toEqual({
+ level: 'syntax',
+ isComponentError: false,
+ message: {
+ content: 'TransformError failed to transform file.',
+ substitutions: [],
+ },
+ stack: [],
+ componentStack: [],
+ category: 'TransformError failed to transform file.',
+ });
+ });
+
+ it('parses a babel transform syntax error', () => {
const error = {
message: `
@@ -269,6 +294,99 @@ describe('parseLogBoxLog', () => {
});
});
+ it('parses a babel codeframe error', () => {
+ const error = {
+ message: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
+
+Please follow the instructions at: fburl.com/rn-remote-assets
+ 197 | });
+ 198 |
+> 199 | export default CrashReactApp;
+ | ^
+ 200 |`,
+ originalMessage: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
+
+Please follow the instructions at: fburl.com/rn-remote-assets
+ 197 | });
+ 198 |
+> 199 | export default CrashReactApp;
+ | ^
+ 200 |`,
+ name: '',
+ isComponentError: false,
+ componentStack: '',
+ stack: [],
+ id: 0,
+ isFatal: true,
+ };
+
+ expect(parseLogBoxException(error)).toEqual({
+ level: 'syntax',
+ isComponentError: false,
+ codeFrame: {
+ fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
+ location: null,
+ content: ` 197 | });
+ 198 |
+> 199 | export default CrashReactApp;
+ | ^
+ 200 |`,
+ },
+ message: {
+ content: `The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
+
+Please follow the instructions at: fburl.com/rn-remote-assets`,
+ substitutions: [],
+ },
+ stack: [],
+ componentStack: [],
+ category: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js-1-1',
+ });
+ });
+
+ it('parses a babel codeframe error with ansi', () => {
+ const error = {
+ message: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
+
+Please follow the instructions at: fburl.com/rn-remote-assets
+ 197 | });
+ 198 |
+> 199 | export default CrashReactApp;
+ | ^
+ 200 |`,
+ originalMessage: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
+
+Please follow the instructions at: fburl.com/rn-remote-assets
+\u001b[0m \u001b[90m 46 | \u001b[39m headline\u001b[33m=\u001b[39m\u001b[32m"CrashReact Error Boundary"\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 47 | \u001b[39m body\u001b[33m=\u001b[39m{\u001b[32m\`\${this.state.errorMessage}\`\u001b[39m}\u001b[0m\n\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 48 | \u001b[39m icon\u001b[33m=\u001b[39m{fbRemoteAsset(\u001b[32m'null_state_glyphs'\u001b[39m\u001b[33m,\u001b[39m {\u001b[0m\n\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 49 | \u001b[39m name\u001b[33m:\u001b[39m \u001b[32m'codexxx'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 50 | \u001b[39m size\u001b[33m:\u001b[39m \u001b[32m'112'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 51 | \u001b[39m })}\u001b[0m`,
+ name: '',
+ isComponentError: false,
+ componentStack: '',
+ stack: [],
+ id: 0,
+ isFatal: true,
+ };
+
+ expect(parseLogBoxException(error)).toEqual({
+ level: 'syntax',
+ isComponentError: false,
+ codeFrame: {
+ fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
+ location: null,
+ content:
+ "\u001b[0m \u001b[90m 46 | \u001b[39m headline\u001b[33m=\u001b[39m\u001b[32m\"CrashReact Error Boundary\"\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 47 | \u001b[39m body\u001b[33m=\u001b[39m{\u001b[32m`${this.state.errorMessage}`\u001b[39m}\u001b[0m\n\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 48 | \u001b[39m icon\u001b[33m=\u001b[39m{fbRemoteAsset(\u001b[32m'null_state_glyphs'\u001b[39m\u001b[33m,\u001b[39m {\u001b[0m\n\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 49 | \u001b[39m name\u001b[33m:\u001b[39m \u001b[32m'codexxx'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 50 | \u001b[39m size\u001b[33m:\u001b[39m \u001b[32m'112'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 51 | \u001b[39m })}\u001b[0m",
+ },
+ message: {
+ content: `The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
+
+Please follow the instructions at: fburl.com/rn-remote-assets`,
+ substitutions: [],
+ },
+ stack: [],
+ componentStack: [],
+ category: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js-1-1',
+ });
+ });
+
it('parses a error log', () => {
const error = {
id: 0,
@@ -402,7 +520,7 @@ describe('parseLogBoxLog', () => {
});
});
- it('a malformed syntax error falls back to a fatal', () => {
+ it('a malformed syntax error falls back to a syntax error', () => {
const error = {
id: 0,
isFatal: true,
@@ -426,7 +544,7 @@ describe('parseLogBoxLog', () => {
};
expect(parseLogBoxException(error)).toEqual({
- level: 'fatal',
+ level: 'syntax',
category:
"TransformError SyntaxError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)",
message: {
diff --git a/Libraries/LogBox/Data/parseLogBoxLog.js b/Libraries/LogBox/Data/parseLogBoxLog.js
index 6a601759cc5..402c9d30341 100644
--- a/Libraries/LogBox/Data/parseLogBoxLog.js
+++ b/Libraries/LogBox/Data/parseLogBoxLog.js
@@ -15,6 +15,9 @@ import stringifySafe from '../../Utilities/stringifySafe';
import type {ExceptionData} from '../../Core/NativeExceptionsManager';
import type {LogBoxLogData} from './LogBoxLog';
+const BABEL_TRANSFORM_ERROR_FORMAT = /^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/;
+const BABEL_CODE_FRAME_ERROR_FORMAT = /^(?:TransformError )?(?:.*): (.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;
+
export type ExtendedExceptionData = ExceptionData & {
isComponentError: boolean,
...
@@ -22,7 +25,7 @@ export type ExtendedExceptionData = ExceptionData & {
export type Category = string;
export type CodeFrame = $ReadOnly<{|
content: string,
- location: {
+ location: ?{
row: number,
column: number,
...
@@ -145,42 +148,77 @@ export function parseLogBoxException(
): LogBoxLogData {
const message =
error.originalMessage != null ? error.originalMessage : 'Unknown';
- const match = message.match(
- /(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/,
- );
- if (!match) {
+ const babelTransformError = message.match(BABEL_TRANSFORM_ERROR_FORMAT);
+ if (babelTransformError) {
+ // Transform errors are thrown from inside the Babel transformer.
+ const [
+ fileName,
+ content,
+ row,
+ column,
+ codeFrame,
+ ] = babelTransformError.slice(1);
+
return {
- level: error.isFatal || error.isComponentError ? 'fatal' : 'error',
- stack: error.stack,
- isComponentError: error.isComponentError,
- componentStack:
- error.componentStack != null
- ? parseComponentStack(error.componentStack)
- : [],
- ...parseCategory([message]),
+ level: 'syntax',
+ stack: [],
+ isComponentError: false,
+ componentStack: [],
+ codeFrame: {
+ fileName,
+ location: {
+ row: parseInt(row, 10),
+ column: parseInt(column, 10),
+ },
+ content: codeFrame,
+ },
+ message: {
+ content,
+ substitutions: [],
+ },
+ category: `${fileName}-${row}-${column}`,
};
}
- const [fileName, content, row, column, codeFrame] = match.slice(1);
- return {
- level: 'syntax',
- stack: [],
- isComponentError: false,
- componentStack: [],
- codeFrame: {
- fileName,
- location: {
- row: parseInt(row, 10),
- column: parseInt(column, 10),
+ const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
+
+ if (babelCodeFrameError) {
+ // Codeframe errors are thrown from any use of buildCodeFrameError.
+ const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
+ return {
+ level: 'syntax',
+ stack: [],
+ isComponentError: false,
+ componentStack: [],
+ codeFrame: {
+ fileName,
+ location: null, // We are not given the location.
+ content: codeFrame,
},
- content: codeFrame,
- },
- message: {
- content,
- substitutions: [],
- },
- category: `${fileName}-${row}-${column}`,
+ message: {
+ content,
+ substitutions: [],
+ },
+ category: `${fileName}-${1}-${1}`,
+ };
+ }
+
+ const level = message.match(/^TransformError /)
+ ? 'syntax'
+ : error.isFatal || error.isComponentError
+ ? 'fatal'
+ : 'error';
+
+ return {
+ level: level,
+ stack: error.stack,
+ isComponentError: error.isComponentError,
+ componentStack:
+ error.componentStack != null
+ ? parseComponentStack(error.componentStack)
+ : [],
+ ...parseCategory([message]),
};
}
diff --git a/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js b/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js
index 50e0dbc1f4d..ee07a2bbc51 100644
--- a/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js
+++ b/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js
@@ -41,6 +41,17 @@ function LogBoxInspectorCodeFrame(props: Props): React.Node {
return codeFrame.fileName;
}
+ function getLocation() {
+ const location = codeFrame.location;
+ if (location != null) {
+ return ` (${location.row}:${
+ location.column + 1 /* Code frame columns are zero indexed */
+ })`;
+ }
+
+ return null;
+ }
+
return (
}>
@@ -59,10 +70,8 @@ function LogBoxInspectorCodeFrame(props: Props): React.Node {
openFileInEditor(codeFrame.fileName, codeFrame.location?.row ?? 0);
}}>
- {getFileName()} ({codeFrame.location.row}:
- {codeFrame.location.column +
- 1 /* Code frame columns are zero indexed */}
- )
+ {getFileName()}
+ {getLocation()}
diff --git a/Libraries/LogBox/UI/LogBoxMessage.js b/Libraries/LogBox/UI/LogBoxMessage.js
index 8bd2740a2ea..dd300c12a02 100644
--- a/Libraries/LogBox/UI/LogBoxMessage.js
+++ b/Libraries/LogBox/UI/LogBoxMessage.js
@@ -24,7 +24,8 @@ type Props = {
...
};
-const cleanContent = content => content.replace(/(Warning|Error): /g, '');
+const cleanContent = content =>
+ content.replace(/^(TransformError |Warning: (Warning: )?|Error: )/g, '');
function LogBoxMessage(props: Props): React.Node {
const {content, substitutions}: Message = props.message;
diff --git a/Libraries/LogBox/UI/__tests__/LogBoxInspectorCodeFrame-test.js b/Libraries/LogBox/UI/__tests__/LogBoxInspectorCodeFrame-test.js
index 7228553951f..16480ba822d 100644
--- a/Libraries/LogBox/UI/__tests__/LogBoxInspectorCodeFrame-test.js
+++ b/Libraries/LogBox/UI/__tests__/LogBoxInspectorCodeFrame-test.js
@@ -41,4 +41,22 @@ describe('LogBoxInspectorCodeFrame', () => {
expect(output).toMatchSnapshot();
});
+
+ it('should render a code frame without a location', () => {
+ const output = render.shallowRender(
+ 199 | export default CrashReactApp;
+ | ^
+ 200 |`,
+ }}
+ />,
+ );
+
+ expect(output).toMatchSnapshot();
+ });
});
diff --git a/Libraries/LogBox/UI/__tests__/LogBoxMessage-test.js b/Libraries/LogBox/UI/__tests__/LogBoxMessage-test.js
index 2e215b55855..98e6e356c47 100644
--- a/Libraries/LogBox/UI/__tests__/LogBoxMessage-test.js
+++ b/Libraries/LogBox/UI/__tests__/LogBoxMessage-test.js
@@ -166,6 +166,20 @@ describe('LogBoxMessage', () => {
expect(output).toMatchSnapshot();
});
+ it('Should strip "TransformError " without breaking substitution', () => {
+ const output = render.shallowRender(
+ ,
+ );
+
+ expect(output).toMatchSnapshot();
+ });
+
it('Should strip "Warning: " without breaking substitution', () => {
const output = render.shallowRender(
CrashReactApp.js
- (
- 199
- :
- 1
- )
+ (199:1)
+
+
+
+
+`;
+
+exports[`LogBoxInspectorCodeFrame should render a code frame without a location 1`] = `
+}
+ heading="Source"
+>
+
+
+
+
+
+
+
+
+ CrashReactApp.js
diff --git a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxMessage-test.js.snap b/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxMessage-test.js.snap
index 94379a0b8fc..9f9260a0429 100644
--- a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxMessage-test.js.snap
+++ b/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxMessage-test.js.snap
@@ -1,5 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`LogBoxMessage Should strip "TransformError " without breaking substitution 1`] = `
+
+
+ normal
+
+
+ substitution
+
+
+ normal
+
+
+`;
+
exports[`LogBoxMessage Should strip "Warning: " without breaking substitution 1`] = `