@expo/metro-runtime 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/build/LoadingView.d.ts +6 -0
  2. package/build/LoadingView.d.ts.map +1 -1
  3. package/build/LoadingView.js +9 -3
  4. package/build/LoadingView.js.map +1 -1
  5. package/build/async-require/fetchAsync.d.ts +6 -0
  6. package/build/async-require/fetchAsync.d.ts.map +1 -1
  7. package/build/async-require/fetchAsync.js +1 -2
  8. package/build/async-require/fetchAsync.js.map +1 -1
  9. package/build/async-require/fetchThenEval.d.ts +1 -6
  10. package/build/async-require/fetchThenEval.d.ts.map +1 -1
  11. package/build/async-require/fetchThenEval.js +2 -32
  12. package/build/async-require/fetchThenEval.js.map +1 -1
  13. package/build/async-require/fetchThenEval.web.js +1 -1
  14. package/build/async-require/fetchThenEval.web.js.map +1 -1
  15. package/build/async-require/fetchThenEvalJs.d.ts +7 -0
  16. package/build/async-require/fetchThenEvalJs.d.ts.map +1 -0
  17. package/build/async-require/fetchThenEvalJs.js +36 -0
  18. package/build/async-require/fetchThenEvalJs.js.map +1 -0
  19. package/build/async-require/index.native.d.ts +7 -0
  20. package/build/async-require/index.native.d.ts.map +1 -0
  21. package/build/async-require/index.native.js +14 -0
  22. package/build/async-require/index.native.js.map +1 -0
  23. package/build/effects.d.ts +0 -1
  24. package/build/effects.js +1 -6
  25. package/build/effects.js.map +1 -1
  26. package/build/error-overlay/Data/parseLogBoxLog.d.ts.map +1 -1
  27. package/build/error-overlay/Data/parseLogBoxLog.js +1 -2
  28. package/build/error-overlay/Data/parseLogBoxLog.js.map +1 -1
  29. package/build/error-overlay/LogBox.web.d.ts.map +1 -1
  30. package/build/error-overlay/LogBox.web.js +1 -2
  31. package/build/error-overlay/LogBox.web.js.map +1 -1
  32. package/build/error-overlay/index.d.ts.map +1 -1
  33. package/build/error-overlay/index.js +1 -0
  34. package/build/error-overlay/index.js.map +1 -1
  35. package/build/getDevServer.d.ts.map +1 -1
  36. package/build/getDevServer.js +2 -6
  37. package/build/getDevServer.js.map +1 -1
  38. package/build/index.d.ts +8 -0
  39. package/build/index.d.ts.map +1 -1
  40. package/build/index.js +10 -8
  41. package/build/index.js.map +1 -1
  42. package/build/setupHMR.js +21 -24
  43. package/build/setupHMR.js.map +1 -1
  44. package/package.json +5 -2
  45. package/src/HMRClient.native.ts +3 -0
  46. package/src/HMRClient.ts +316 -0
  47. package/src/LoadingView.native.ts +3 -0
  48. package/src/LoadingView.ts +24 -0
  49. package/src/__mocks__/LoadingView.ts +4 -0
  50. package/src/async-require/buildAsyncRequire.ts +34 -0
  51. package/src/async-require/buildUrlForBundle.native.ts +28 -0
  52. package/src/async-require/buildUrlForBundle.ts +18 -0
  53. package/src/async-require/fetchAsync.native.ts +72 -0
  54. package/src/async-require/fetchAsync.ts +19 -0
  55. package/src/async-require/fetchThenEval.ts +1 -0
  56. package/src/async-require/fetchThenEval.web.ts +70 -0
  57. package/src/async-require/fetchThenEvalJs.ts +39 -0
  58. package/src/async-require/index.native.ts +15 -0
  59. package/src/async-require/index.ts +10 -0
  60. package/src/async-require/loadBundle.ts +46 -0
  61. package/src/effects.native.ts +0 -0
  62. package/src/effects.ts +11 -0
  63. package/src/error-overlay/Data/LogBoxData.tsx +438 -0
  64. package/src/error-overlay/Data/LogBoxLog.ts +221 -0
  65. package/src/error-overlay/Data/LogBoxSymbolication.tsx +64 -0
  66. package/src/error-overlay/Data/LogContext.tsx +41 -0
  67. package/src/error-overlay/Data/parseLogBoxLog.tsx +342 -0
  68. package/src/error-overlay/ErrorOverlay.tsx +191 -0
  69. package/src/error-overlay/LogBox.ts +51 -0
  70. package/src/error-overlay/LogBox.web.ts +174 -0
  71. package/src/error-overlay/UI/AnsiHighlight.tsx +96 -0
  72. package/src/error-overlay/UI/LogBoxButton.tsx +63 -0
  73. package/src/error-overlay/UI/LogBoxMessage.tsx +73 -0
  74. package/src/error-overlay/UI/LogBoxStyle.ts +64 -0
  75. package/src/error-overlay/UI/constants.ts +7 -0
  76. package/src/error-overlay/formatProjectFilePath.ts +38 -0
  77. package/src/error-overlay/index.tsx +34 -0
  78. package/src/error-overlay/modules/ExceptionsManager/index.native.ts +4 -0
  79. package/src/error-overlay/modules/ExceptionsManager/index.ts +82 -0
  80. package/src/error-overlay/modules/NativeLogBox/index.native.ts +3 -0
  81. package/src/error-overlay/modules/NativeLogBox/index.tsx +27 -0
  82. package/src/error-overlay/modules/openFileInEditor/index.native.ts +3 -0
  83. package/src/error-overlay/modules/openFileInEditor/index.ts +16 -0
  84. package/src/error-overlay/modules/parseErrorStack/index.ts +26 -0
  85. package/src/error-overlay/modules/parseErrorStack/parseHermesStack.ts +3 -0
  86. package/src/error-overlay/modules/stringifySafe/index.ts +115 -0
  87. package/src/error-overlay/modules/symbolicateStackTrace/index.native.ts +3 -0
  88. package/src/error-overlay/modules/symbolicateStackTrace/index.ts +39 -0
  89. package/src/error-overlay/overlay/LogBoxInspectorCodeFrame.tsx +102 -0
  90. package/src/error-overlay/overlay/LogBoxInspectorFooter.tsx +111 -0
  91. package/src/error-overlay/overlay/LogBoxInspectorHeader.tsx +167 -0
  92. package/src/error-overlay/overlay/LogBoxInspectorMessageHeader.tsx +116 -0
  93. package/src/error-overlay/overlay/LogBoxInspectorSection.tsx +52 -0
  94. package/src/error-overlay/overlay/LogBoxInspectorSourceMapStatus.tsx +125 -0
  95. package/src/error-overlay/overlay/LogBoxInspectorStackFrame.tsx +89 -0
  96. package/src/error-overlay/overlay/LogBoxInspectorStackFrames.tsx +201 -0
  97. package/src/error-overlay/toast/ErrorToast.tsx +167 -0
  98. package/src/error-overlay/toast/ErrorToastContainer.tsx +9 -0
  99. package/src/error-overlay/toast/ErrorToastContainer.web.tsx +92 -0
  100. package/src/error-overlay/toast/ErrorToastMessage.tsx +28 -0
  101. package/src/error-overlay/useRejectionHandler.ts +61 -0
  102. package/src/getDevServer.native.ts +3 -0
  103. package/src/getDevServer.ts +34 -0
  104. package/src/index.ts +12 -0
  105. package/src/location/Location.native.ts +201 -0
  106. package/src/location/Location.ts +3 -0
  107. package/src/location/install.native.ts +90 -0
  108. package/src/location/install.ts +0 -0
  109. package/src/messageSocket.ts +25 -0
  110. package/src/setupFastRefresh.ts +30 -0
  111. package/src/setupHMR.ts +28 -0
  112. package/src/symbolicate.ts +6 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Copyright (c) 650 Industries.
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import * as LogBoxSymbolication from './LogBoxSymbolication';
10
+ import type { Stack } from './LogBoxSymbolication';
11
+ import type { Category, Message, ComponentStack, CodeFrame } from './parseLogBoxLog';
12
+
13
+ type SymbolicationStatus = 'NONE' | 'PENDING' | 'COMPLETE' | 'FAILED';
14
+
15
+ export type LogLevel = 'warn' | 'error' | 'fatal' | 'syntax' | 'static';
16
+
17
+ export type LogBoxLogData = {
18
+ level: LogLevel;
19
+ type?: string;
20
+ message: Message;
21
+ stack: Stack;
22
+ category: string;
23
+ componentStack: ComponentStack;
24
+ codeFrame?: CodeFrame;
25
+ isComponentError: boolean;
26
+ };
27
+
28
+ export type StackType = 'stack' | 'component';
29
+
30
+ function componentStackToStack(componentStack: ComponentStack): Stack {
31
+ return componentStack.map((stack) => ({
32
+ file: stack.fileName,
33
+ methodName: stack.content,
34
+ lineNumber: stack.location?.row ?? 0,
35
+ column: stack.location?.column ?? 0,
36
+ arguments: [],
37
+ }));
38
+ }
39
+
40
+ type SymbolicationCallback = (status: SymbolicationStatus) => void;
41
+
42
+ type SymbolicationResult =
43
+ | { error: null; stack: null; status: 'NONE' }
44
+ | { error: null; stack: null; status: 'PENDING' }
45
+ | { error: null; stack: Stack; status: 'COMPLETE' }
46
+ | { error: Error; stack: null; status: 'FAILED' };
47
+
48
+ export class LogBoxLog {
49
+ message: Message;
50
+ type: string;
51
+ category: Category;
52
+ componentStack: ComponentStack;
53
+ stack: Stack;
54
+ count: number;
55
+ level: LogLevel;
56
+ codeFrame?: CodeFrame;
57
+ isComponentError: boolean;
58
+ symbolicated: Record<StackType, SymbolicationResult> = {
59
+ stack: {
60
+ error: null,
61
+ stack: null,
62
+ status: 'NONE',
63
+ },
64
+ component: {
65
+ error: null,
66
+ stack: null,
67
+ status: 'NONE',
68
+ },
69
+ };
70
+
71
+ private callbacks: Map<StackType, Set<SymbolicationCallback>> = new Map();
72
+
73
+ constructor(
74
+ data: LogBoxLogData & {
75
+ symbolicated?: Record<StackType, SymbolicationResult>;
76
+ }
77
+ ) {
78
+ this.level = data.level;
79
+ this.type = data.type ?? 'error';
80
+ this.message = data.message;
81
+ this.stack = data.stack;
82
+ this.category = data.category;
83
+ this.componentStack = data.componentStack;
84
+ this.codeFrame = data.codeFrame;
85
+ this.isComponentError = data.isComponentError;
86
+ this.count = 1;
87
+ this.symbolicated = data.symbolicated ?? this.symbolicated;
88
+ }
89
+
90
+ incrementCount(): void {
91
+ this.count += 1;
92
+ }
93
+
94
+ getAvailableStack(type: StackType): Stack | null {
95
+ if (this.symbolicated[type].status === 'COMPLETE') {
96
+ return this.symbolicated[type].stack;
97
+ }
98
+ return this.getStack(type);
99
+ }
100
+
101
+ private flushCallbacks(type: StackType): void {
102
+ const callbacks = this.callbacks.get(type);
103
+ const status = this.symbolicated[type].status;
104
+ if (callbacks) {
105
+ for (const callback of callbacks) {
106
+ callback(status);
107
+ }
108
+ callbacks.clear();
109
+ }
110
+ }
111
+
112
+ private pushCallback(type: StackType, callback: SymbolicationCallback): void {
113
+ let callbacks = this.callbacks.get(type);
114
+ if (!callbacks) {
115
+ callbacks = new Set();
116
+ this.callbacks.set(type, callbacks);
117
+ }
118
+ callbacks.add(callback);
119
+ }
120
+
121
+ retrySymbolicate(type: StackType, callback?: (status: SymbolicationStatus) => void): void {
122
+ this._symbolicate(type, true, callback);
123
+ }
124
+
125
+ symbolicate(type: StackType, callback?: (status: SymbolicationStatus) => void): void {
126
+ this._symbolicate(type, false, callback);
127
+ }
128
+
129
+ private _symbolicate(
130
+ type: StackType,
131
+ retry: boolean,
132
+ callback?: (status: SymbolicationStatus) => void
133
+ ): void {
134
+ if (callback) {
135
+ this.pushCallback(type, callback);
136
+ }
137
+ const status = this.symbolicated[type].status;
138
+
139
+ if (status === 'COMPLETE') {
140
+ return this.flushCallbacks(type);
141
+ }
142
+
143
+ if (retry) {
144
+ LogBoxSymbolication.deleteStack(this.getStack(type));
145
+ this.handleSymbolicate(type);
146
+ } else {
147
+ if (status === 'NONE') {
148
+ this.handleSymbolicate(type);
149
+ }
150
+ }
151
+ }
152
+
153
+ private componentStackCache: Stack | null = null;
154
+
155
+ private getStack(type: StackType): Stack {
156
+ if (type === 'component') {
157
+ if (this.componentStackCache == null) {
158
+ this.componentStackCache = componentStackToStack(this.componentStack);
159
+ }
160
+ return this.componentStackCache;
161
+ }
162
+ return this.stack;
163
+ }
164
+
165
+ private handleSymbolicate(type: StackType): void {
166
+ if (type === 'component' && !this.componentStack?.length) {
167
+ return;
168
+ }
169
+
170
+ if (this.symbolicated[type].status !== 'PENDING') {
171
+ this.updateStatus(type, null, null, null);
172
+ LogBoxSymbolication.symbolicate(this.getStack(type)).then(
173
+ (data) => {
174
+ this.updateStatus(type, null, data?.stack, data?.codeFrame);
175
+ },
176
+ (error) => {
177
+ this.updateStatus(type, error, null, null);
178
+ }
179
+ );
180
+ }
181
+ }
182
+
183
+ private updateStatus(
184
+ type: StackType,
185
+ error?: Error | null,
186
+ stack?: Stack | null,
187
+ codeFrame?: CodeFrame | null
188
+ ): void {
189
+ const lastStatus = this.symbolicated[type].status;
190
+ if (error != null) {
191
+ this.symbolicated[type] = {
192
+ error,
193
+ stack: null,
194
+ status: 'FAILED',
195
+ };
196
+ } else if (stack != null) {
197
+ if (codeFrame) {
198
+ this.codeFrame = codeFrame;
199
+ }
200
+
201
+ this.symbolicated[type] = {
202
+ error: null,
203
+ stack,
204
+ status: 'COMPLETE',
205
+ };
206
+ } else {
207
+ this.symbolicated[type] = {
208
+ error: null,
209
+ stack: null,
210
+ status: 'PENDING',
211
+ };
212
+ }
213
+
214
+ const status = this.symbolicated[type].status;
215
+ if (lastStatus !== status) {
216
+ if (['COMPLETE', 'FAILED'].includes(status)) {
217
+ this.flushCallbacks(type);
218
+ }
219
+ }
220
+ }
221
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Copyright (c) 650 Industries.
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { StackFrame as UpstreamStackFrame } from 'stacktrace-parser';
10
+
11
+ import symbolicateStackTrace from '../modules/symbolicateStackTrace';
12
+
13
+ type SymbolicatedStackTrace = any;
14
+
15
+ type StackFrame = UpstreamStackFrame & { collapse?: boolean };
16
+
17
+ export type Stack = StackFrame[];
18
+
19
+ const cache: Map<Stack, Promise<SymbolicatedStackTrace>> = new Map();
20
+
21
+ /**
22
+ * Sanitize because sometimes, `symbolicateStackTrace` gives us invalid values.
23
+ */
24
+ const sanitize = ({
25
+ stack: maybeStack,
26
+ codeFrame,
27
+ }: SymbolicatedStackTrace): SymbolicatedStackTrace => {
28
+ if (!Array.isArray(maybeStack)) {
29
+ throw new Error('Expected stack to be an array.');
30
+ }
31
+ const stack: StackFrame[] = [];
32
+ for (const maybeFrame of maybeStack) {
33
+ let collapse = false;
34
+ if ('collapse' in maybeFrame) {
35
+ if (typeof maybeFrame.collapse !== 'boolean') {
36
+ throw new Error('Expected stack frame `collapse` to be a boolean.');
37
+ }
38
+ collapse = maybeFrame.collapse;
39
+ }
40
+ stack.push({
41
+ arguments: [],
42
+ column: maybeFrame.column,
43
+ file: maybeFrame.file,
44
+ lineNumber: maybeFrame.lineNumber,
45
+ methodName: maybeFrame.methodName,
46
+ collapse,
47
+ });
48
+ }
49
+ return { stack, codeFrame };
50
+ };
51
+
52
+ export function deleteStack(stack: Stack): void {
53
+ cache.delete(stack);
54
+ }
55
+
56
+ export function symbolicate(stack: Stack): Promise<SymbolicatedStackTrace> {
57
+ let promise = cache.get(stack);
58
+ if (promise == null) {
59
+ promise = symbolicateStackTrace(stack).then(sanitize);
60
+ cache.set(stack, promise);
61
+ }
62
+
63
+ return promise;
64
+ }
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import { Platform } from 'react-native';
3
+
4
+ import { LogBoxLog } from './LogBoxLog';
5
+
6
+ // Context provider for Array<LogBoxLog>
7
+
8
+ export const LogContext = React.createContext<{
9
+ selectedLogIndex: number;
10
+ isDisabled: boolean;
11
+ logs: LogBoxLog[];
12
+ } | null>(null);
13
+
14
+ export function useLogs(): {
15
+ selectedLogIndex: number;
16
+ isDisabled: boolean;
17
+ logs: LogBoxLog[];
18
+ } {
19
+ const logs = React.useContext(LogContext);
20
+ if (!logs) {
21
+ if (Platform.OS === 'web' && typeof window !== 'undefined') {
22
+ // Logbox data that is pre-fetched on the dev server and rendered here.
23
+ const expoCliStaticErrorElement = document.getElementById('_expo-static-error');
24
+ if (expoCliStaticErrorElement?.textContent) {
25
+ const raw = JSON.parse(expoCliStaticErrorElement.textContent);
26
+ return {
27
+ ...raw,
28
+ logs: raw.logs.map((raw: any) => new LogBoxLog(raw)),
29
+ };
30
+ }
31
+ }
32
+
33
+ throw new Error('useLogs must be used within a LogProvider');
34
+ }
35
+ return logs;
36
+ }
37
+
38
+ export function useSelectedLog() {
39
+ const { selectedLogIndex, logs } = useLogs();
40
+ return logs[selectedLogIndex];
41
+ }
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Copyright (c) 650 Industries.
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import type { LogBoxLogData } from './LogBoxLog';
10
+ import parseErrorStack from '../modules/parseErrorStack';
11
+ import stringifySafe from '../modules/stringifySafe';
12
+ type ExceptionData = any;
13
+
14
+ const BABEL_TRANSFORM_ERROR_FORMAT =
15
+ /^(?:TransformError )?(?:SyntaxError: |ReferenceError: )(.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/;
16
+ const BABEL_CODE_FRAME_ERROR_FORMAT =
17
+ /^(?:TransformError )?(?:.*):? (?:.*?)(\/.*): ([\s\S]+?)\n([ >]{2}[\d\s]+ \|[\s\S]+|\u{001b}[\s\S]+)/u;
18
+ const METRO_ERROR_FORMAT =
19
+ /^(?:InternalError Metro has encountered an error:) (.*): (.*) \((\d+):(\d+)\)\n\n([\s\S]+)/u;
20
+
21
+ export type ExtendedExceptionData = ExceptionData & {
22
+ isComponentError: boolean;
23
+ [key: string]: any;
24
+ };
25
+ export type Category = string;
26
+ export type CodeFrame = {
27
+ content: string;
28
+ location?: {
29
+ row: number;
30
+ column: number;
31
+ [key: string]: any;
32
+ } | null;
33
+ fileName: string;
34
+
35
+ // TODO: When React switched to using call stack frames,
36
+ // we gained the ability to use the collapse flag, but
37
+ // it is not integrated into the LogBox UI.
38
+ collapse?: boolean;
39
+ };
40
+
41
+ export type Message = {
42
+ content: string;
43
+ substitutions: {
44
+ length: number;
45
+ offset: number;
46
+ }[];
47
+ };
48
+
49
+ export type ComponentStack = CodeFrame[];
50
+
51
+ const SUBSTITUTION = '\ufeff%s';
52
+
53
+ export function parseInterpolation(args: readonly any[]): {
54
+ category: Category;
55
+ message: Message;
56
+ } {
57
+ const categoryParts: string[] = [];
58
+ const contentParts: string[] = [];
59
+ const substitutionOffsets: { length: number; offset: number }[] = [];
60
+
61
+ const remaining = [...args];
62
+ if (typeof remaining[0] === 'string') {
63
+ const formatString = String(remaining.shift());
64
+ const formatStringParts = formatString.split('%s');
65
+ const substitutionCount = formatStringParts.length - 1;
66
+ const substitutions = remaining.splice(0, substitutionCount);
67
+
68
+ let categoryString = '';
69
+ let contentString = '';
70
+
71
+ let substitutionIndex = 0;
72
+ for (const formatStringPart of formatStringParts) {
73
+ categoryString += formatStringPart;
74
+ contentString += formatStringPart;
75
+
76
+ if (substitutionIndex < substitutionCount) {
77
+ if (substitutionIndex < substitutions.length) {
78
+ // Don't stringify a string type.
79
+ // It adds quotation mark wrappers around the string,
80
+ // which causes the LogBox to look odd.
81
+ const substitution =
82
+ typeof substitutions[substitutionIndex] === 'string'
83
+ ? substitutions[substitutionIndex]
84
+ : stringifySafe(substitutions[substitutionIndex]);
85
+ substitutionOffsets.push({
86
+ length: substitution.length,
87
+ offset: contentString.length,
88
+ });
89
+
90
+ categoryString += SUBSTITUTION;
91
+ contentString += substitution;
92
+ } else {
93
+ substitutionOffsets.push({
94
+ length: 2,
95
+ offset: contentString.length,
96
+ });
97
+
98
+ categoryString += '%s';
99
+ contentString += '%s';
100
+ }
101
+
102
+ substitutionIndex++;
103
+ }
104
+ }
105
+
106
+ categoryParts.push(categoryString);
107
+ contentParts.push(contentString);
108
+ }
109
+
110
+ const remainingArgs = remaining.map((arg) => {
111
+ // Don't stringify a string type.
112
+ // It adds quotation mark wrappers around the string,
113
+ // which causes the LogBox to look odd.
114
+ return typeof arg === 'string' ? arg : stringifySafe(arg);
115
+ });
116
+ categoryParts.push(...remainingArgs);
117
+ contentParts.push(...remainingArgs);
118
+
119
+ return {
120
+ category: categoryParts.join(' '),
121
+ message: {
122
+ content: contentParts.join(' '),
123
+ substitutions: substitutionOffsets,
124
+ },
125
+ };
126
+ }
127
+
128
+ function isComponentStack(consoleArgument: string) {
129
+ const isOldComponentStackFormat = / {4}in/.test(consoleArgument);
130
+ const isNewComponentStackFormat = / {4}at/.test(consoleArgument);
131
+ const isNewJSCComponentStackFormat = /@.*\n/.test(consoleArgument);
132
+
133
+ return isOldComponentStackFormat || isNewComponentStackFormat || isNewJSCComponentStackFormat;
134
+ }
135
+
136
+ export function parseComponentStack(message: string): ComponentStack {
137
+ // In newer versions of React, the component stack is formatted as a call stack frame.
138
+ // First try to parse the component stack as a call stack frame, and if that doesn't
139
+ // work then we'll fallback to the old custom component stack format parsing.
140
+ const stack = parseErrorStack(message);
141
+ if (stack && stack.length > 0) {
142
+ return stack.map((frame) => ({
143
+ content: frame.methodName,
144
+ collapse: frame.collapse || false,
145
+ fileName: frame.file == null ? 'unknown' : frame.file,
146
+ location: {
147
+ column: frame.column == null ? -1 : frame.column,
148
+ row: frame.lineNumber == null ? -1 : frame.lineNumber,
149
+ },
150
+ }));
151
+ }
152
+
153
+ return message
154
+ .split(/\n {4}in /g)
155
+ .map((s) => {
156
+ if (!s) {
157
+ return null;
158
+ }
159
+ const match = s.match(/(.*) \(at (.*\.js):([\d]+)\)/);
160
+ if (!match) {
161
+ return null;
162
+ }
163
+
164
+ const [content, fileName, row] = match.slice(1);
165
+ return {
166
+ content,
167
+ fileName,
168
+ location: { column: -1, row: parseInt(row, 10) },
169
+ };
170
+ })
171
+ .filter(Boolean) as ComponentStack;
172
+ }
173
+
174
+ export function parseLogBoxException(error: ExtendedExceptionData): LogBoxLogData {
175
+ const message = error.originalMessage != null ? error.originalMessage : 'Unknown';
176
+
177
+ const metroInternalError = message.match(METRO_ERROR_FORMAT);
178
+ if (metroInternalError) {
179
+ const [content, fileName, row, column, codeFrame] = metroInternalError.slice(1);
180
+
181
+ return {
182
+ level: 'fatal',
183
+ type: 'Metro Error',
184
+ stack: [],
185
+ isComponentError: false,
186
+ componentStack: [],
187
+ codeFrame: {
188
+ fileName,
189
+ location: {
190
+ row: parseInt(row, 10),
191
+ column: parseInt(column, 10),
192
+ },
193
+ content: codeFrame,
194
+ },
195
+ message: {
196
+ content,
197
+ substitutions: [],
198
+ },
199
+ category: `${fileName}-${row}-${column}`,
200
+ };
201
+ }
202
+
203
+ const babelTransformError = message.match(BABEL_TRANSFORM_ERROR_FORMAT);
204
+ if (babelTransformError) {
205
+ // Transform errors are thrown from inside the Babel transformer.
206
+ const [fileName, content, row, column, codeFrame] = babelTransformError.slice(1);
207
+
208
+ return {
209
+ level: 'syntax',
210
+ stack: [],
211
+ isComponentError: false,
212
+ componentStack: [],
213
+ codeFrame: {
214
+ fileName,
215
+ location: {
216
+ row: parseInt(row, 10),
217
+ column: parseInt(column, 10),
218
+ },
219
+ content: codeFrame,
220
+ },
221
+ message: {
222
+ content,
223
+ substitutions: [],
224
+ },
225
+ category: `${fileName}-${row}-${column}`,
226
+ };
227
+ }
228
+
229
+ const babelCodeFrameError = message.match(BABEL_CODE_FRAME_ERROR_FORMAT);
230
+
231
+ if (babelCodeFrameError) {
232
+ // Codeframe errors are thrown from any use of buildCodeFrameError.
233
+ const [fileName, content, codeFrame] = babelCodeFrameError.slice(1);
234
+ return {
235
+ level: 'syntax',
236
+ stack: [],
237
+ isComponentError: false,
238
+ componentStack: [],
239
+ codeFrame: {
240
+ fileName,
241
+ location: null, // We are not given the location.
242
+ content: codeFrame,
243
+ },
244
+ message: {
245
+ content,
246
+ substitutions: [],
247
+ },
248
+ category: `${fileName}-${1}-${1}`,
249
+ };
250
+ }
251
+
252
+ if (message.match(/^TransformError /)) {
253
+ return {
254
+ level: 'syntax',
255
+ stack: error.stack,
256
+ isComponentError: error.isComponentError,
257
+ componentStack: [],
258
+ message: {
259
+ content: message,
260
+ substitutions: [],
261
+ },
262
+ category: message,
263
+ };
264
+ }
265
+
266
+ const componentStack = error.componentStack;
267
+ if (error.isFatal || error.isComponentError) {
268
+ return {
269
+ level: 'fatal',
270
+ stack: error.stack,
271
+ isComponentError: error.isComponentError,
272
+ componentStack: componentStack != null ? parseComponentStack(componentStack) : [],
273
+ ...parseInterpolation([message]),
274
+ };
275
+ }
276
+
277
+ if (componentStack != null) {
278
+ // It is possible that console errors have a componentStack.
279
+ return {
280
+ level: 'error',
281
+ stack: error.stack,
282
+ isComponentError: error.isComponentError,
283
+ componentStack: parseComponentStack(componentStack),
284
+ ...parseInterpolation([message]),
285
+ };
286
+ }
287
+
288
+ // Most `console.error` calls won't have a componentStack. We parse them like
289
+ // regular logs which have the component stack burried in the message.
290
+ return {
291
+ level: 'error',
292
+ stack: error.stack,
293
+ isComponentError: error.isComponentError,
294
+ ...parseLogBoxLog([message]),
295
+ };
296
+ }
297
+
298
+ export function parseLogBoxLog(args: readonly any[]): {
299
+ componentStack: ComponentStack;
300
+ category: Category;
301
+ message: Message;
302
+ } {
303
+ const message = args[0];
304
+ let argsWithoutComponentStack: any[] = [];
305
+ let componentStack: ComponentStack = [];
306
+
307
+ // Extract component stack from warnings like "Some warning%s".
308
+ if (typeof message === 'string' && message.slice(-2) === '%s' && args.length > 0) {
309
+ const lastArg = args[args.length - 1];
310
+ if (typeof lastArg === 'string' && isComponentStack(lastArg)) {
311
+ argsWithoutComponentStack = args.slice(0, -1);
312
+ argsWithoutComponentStack[0] = message.slice(0, -2);
313
+ componentStack = parseComponentStack(lastArg);
314
+ }
315
+ }
316
+
317
+ if (componentStack.length === 0) {
318
+ // Try finding the component stack elsewhere.
319
+ for (const arg of args) {
320
+ if (typeof arg === 'string' && isComponentStack(arg)) {
321
+ // Strip out any messages before the component stack.
322
+ let messageEndIndex = arg.search(/\n {4}(in|at) /);
323
+ if (messageEndIndex < 0) {
324
+ // Handle JSC component stacks.
325
+ messageEndIndex = arg.search(/\n/);
326
+ }
327
+ if (messageEndIndex > 0) {
328
+ argsWithoutComponentStack.push(arg.slice(0, messageEndIndex));
329
+ }
330
+
331
+ componentStack = parseComponentStack(arg);
332
+ } else {
333
+ argsWithoutComponentStack.push(arg);
334
+ }
335
+ }
336
+ }
337
+
338
+ return {
339
+ ...parseInterpolation(argsWithoutComponentStack),
340
+ componentStack,
341
+ };
342
+ }