Organize component stack regexes better (#43371)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43371

This file has a lot of regexes, let's organize and comment them all.

Changelog: [Internal]

Reviewed By: yungsters

Differential Revision: D54638520

fbshipit-source-id: eed61133758ccefd2a640f121c4da214bcad4880
This commit is contained in:
Rick Hanlon
2024-03-07 23:15:55 -08:00
committed by Facebook GitHub Bot
parent adaf5eba3e
commit ed32b4ba0a
+68 -21
View File
@@ -18,11 +18,62 @@ import ansiRegex from 'ansi-regex';
const ANSI_REGEX = ansiRegex().source;
const BABEL_TRANSFORM_ERROR_FORMAT =
const RE_TRANSFORM_ERROR = /^TransformError /;
const RE_COMPONENT_STACK_LINE = /\n {4}(in|at) /;
const RE_COMPONENT_STACK_LINE_GLOBAL = /\n {4}(in|at) /g;
const RE_COMPONENT_STACK_LINE_OLD = / {4}in/;
const RE_COMPONENT_STACK_LINE_NEW = / {4}at/;
const RE_COMPONENT_STACK_LINE_STACK_FRAME = /@.*\n/;
// "TransformError " (Optional) and either "SyntaxError: " or "ReferenceError: "
// Capturing groups:
// 1: error message
// 2: file path
// 3: line number
// 4: column number
// \n\n
// 5: code frame
const RE_BABEL_TRANSFORM_ERROR_FORMAT =
/^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/;
// Capturing groups:
// 1: component name
// "at"
// 2: file path including extension
// 3: line number
const RE_COMPONENT_STACK_WITH_SOURCE =
/(.*) \(at (.*\.(?:js|jsx|ts|tsx)):([\d]+)\)/;
// Capturing groups:
// 1: component name
// "at"
// 2: parent component name
const RE_COMPONENT_STACK_NO_SOURCE = /(.*) \(created by .*\)/;
// Capturing groups:
// - non-capturing "TransformError " (optional)
// - non-capturing Error message
// 1: file path
// 2: file name
// 3: error message
// 4: code frame, which includes code snippet indicators or terminal escape sequences for formatting.
const RE_BABEL_CODE_FRAME_ERROR_FORMAT =
// eslint-disable-next-line no-control-regex
/^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;
// Capturing groups:
// - non-capturing "InternalError Metro has encountered an error:"
// 1: error title
// 2: error message
// 3: file path
// 4: line number
// 5: column number
// 6: code frame, which includes code snippet indicators or terminal escape sequences for formatting.
const RE_METRO_ERROR_FORMAT =
/^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/u;
// https://github.com/babel/babel/blob/33dbb85e9e9fe36915273080ecc42aee62ed0ade/packages/babel-code-frame/src/index.ts#L183-L184
const BABEL_CODE_FRAME_MARKER_PATTERN = new RegExp(
const RE_BABEL_CODE_FRAME_MARKER_PATTERN = new RegExp(
[
// Beginning of a line (per 'm' flag)
'^',
@@ -42,13 +93,6 @@ const BABEL_CODE_FRAME_MARKER_PATTERN = new RegExp(
'm',
);
const BABEL_CODE_FRAME_ERROR_FORMAT =
// eslint-disable-next-line no-control-regex
/^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;
const METRO_ERROR_FORMAT =
/^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/u;
export type ExtendedExceptionData = ExceptionData & {
isComponentError: boolean,
...
@@ -158,9 +202,12 @@ export function parseInterpolation(args: $ReadOnlyArray<mixed>): $ReadOnly<{|
}
function isComponentStack(consoleArgument: string) {
const isOldComponentStackFormat = / {4}in/.test(consoleArgument);
const isNewComponentStackFormat = / {4}at/.test(consoleArgument);
const isNewJSCComponentStackFormat = /@.*\n/.test(consoleArgument);
const isOldComponentStackFormat =
RE_COMPONENT_STACK_LINE_OLD.test(consoleArgument);
const isNewComponentStackFormat =
RE_COMPONENT_STACK_LINE_NEW.test(consoleArgument);
const isNewJSCComponentStackFormat =
RE_COMPONENT_STACK_LINE_STACK_FRAME.test(consoleArgument);
return (
isOldComponentStackFormat ||
@@ -187,12 +234,12 @@ export function parseComponentStack(message: string): ComponentStack {
}
return message
.split(/\n {4}in /g)
.split(RE_COMPONENT_STACK_LINE_GLOBAL)
.map(s => {
if (!s) {
return null;
}
const match = s.match(/(.*) \(at (.*\.(?:js|jsx|ts|tsx)):([\d]+)\)/);
const match = s.match(RE_COMPONENT_STACK_WITH_SOURCE);
if (match) {
let [content, fileName, row] = match.slice(1);
return {
@@ -203,7 +250,7 @@ export function parseComponentStack(message: string): ComponentStack {
}
// In some cases, the component stack doesn't have a source.
const matchWithoutSource = s.match(/(.*) \(created by .*\)/);
const matchWithoutSource = s.match(RE_COMPONENT_STACK_NO_SOURCE);
if (matchWithoutSource) {
return {
content: matchWithoutSource[1],
@@ -223,7 +270,7 @@ export function parseLogBoxException(
const message =
error.originalMessage != null ? error.originalMessage : 'Unknown';
const metroInternalError = message.match(METRO_ERROR_FORMAT);
const metroInternalError = message.match(RE_METRO_ERROR_FORMAT);
if (metroInternalError) {
const [content, fileName, row, column, codeFrame] =
metroInternalError.slice(1);
@@ -251,7 +298,7 @@ export function parseLogBoxException(
};
}
const babelTransformError = message.match(BABEL_TRANSFORM_ERROR_FORMAT);
const babelTransformError = message.match(RE_BABEL_TRANSFORM_ERROR_FORMAT);
if (babelTransformError) {
// Transform errors are thrown from inside the Babel transformer.
const [fileName, content, row, column, codeFrame] =
@@ -281,8 +328,8 @@ export function parseLogBoxException(
// Perform a cheap match first before trying to parse the full message, which
// can get expensive for arbitrary input.
if (BABEL_CODE_FRAME_MARKER_PATTERN.test(message)) {
const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
if (RE_BABEL_CODE_FRAME_MARKER_PATTERN.test(message)) {
const babelCodeFrameError = message.match(RE_BABEL_CODE_FRAME_ERROR_FORMAT);
if (babelCodeFrameError) {
// Codeframe errors are thrown from any use of buildCodeFrameError.
@@ -307,7 +354,7 @@ export function parseLogBoxException(
}
}
if (message.match(/^TransformError /)) {
if (message.match(RE_TRANSFORM_ERROR)) {
return {
level: 'syntax',
stack: error.stack,
@@ -386,7 +433,7 @@ export function parseLogBoxLog(args: $ReadOnlyArray<mixed>): {|
for (const arg of args) {
if (typeof arg === 'string' && isComponentStack(arg)) {
// Strip out any messages before the component stack.
let messageEndIndex = arg.search(/\n {4}(in|at) /);
let messageEndIndex = arg.search(RE_COMPONENT_STACK_LINE);
if (messageEndIndex < 0) {
// Handle JSC component stacks.
messageEndIndex = arg.search(/\n/);