@expo/metro-runtime 3.0.0 → 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 (219) hide show
  1. package/build/HMRClient.js +43 -15
  2. package/build/HMRClient.js.map +1 -1
  3. package/build/HMRClient.native.js +3 -1
  4. package/build/HMRClient.native.js.map +1 -1
  5. package/build/LoadingView.d.ts +6 -0
  6. package/build/LoadingView.d.ts.map +1 -1
  7. package/build/LoadingView.js +12 -4
  8. package/build/LoadingView.js.map +1 -1
  9. package/build/LoadingView.native.js +7 -2
  10. package/build/LoadingView.native.js.map +1 -1
  11. package/build/async-require/buildAsyncRequire.js +7 -3
  12. package/build/async-require/buildAsyncRequire.js.map +1 -1
  13. package/build/async-require/buildUrlForBundle.js +5 -1
  14. package/build/async-require/buildUrlForBundle.js.map +1 -1
  15. package/build/async-require/buildUrlForBundle.native.js +5 -1
  16. package/build/async-require/buildUrlForBundle.native.js.map +1 -1
  17. package/build/async-require/fetchAsync.d.ts +6 -0
  18. package/build/async-require/fetchAsync.d.ts.map +1 -1
  19. package/build/async-require/fetchAsync.js +6 -3
  20. package/build/async-require/fetchAsync.js.map +1 -1
  21. package/build/async-require/fetchAsync.native.js +13 -6
  22. package/build/async-require/fetchAsync.native.js.map +1 -1
  23. package/build/async-require/fetchThenEval.d.ts +1 -6
  24. package/build/async-require/fetchThenEval.d.ts.map +1 -1
  25. package/build/async-require/fetchThenEval.js +5 -31
  26. package/build/async-require/fetchThenEval.js.map +1 -1
  27. package/build/async-require/fetchThenEval.web.js +6 -2
  28. package/build/async-require/fetchThenEval.web.js.map +1 -1
  29. package/build/async-require/fetchThenEvalJs.d.ts +7 -0
  30. package/build/async-require/fetchThenEvalJs.d.ts.map +1 -0
  31. package/build/async-require/fetchThenEvalJs.js +36 -0
  32. package/build/async-require/fetchThenEvalJs.js.map +1 -0
  33. package/build/async-require/index.js +4 -2
  34. package/build/async-require/index.js.map +1 -1
  35. package/build/async-require/index.native.d.ts +7 -0
  36. package/build/async-require/index.native.d.ts.map +1 -0
  37. package/build/async-require/index.native.js +14 -0
  38. package/build/async-require/index.native.js.map +1 -0
  39. package/build/async-require/loadBundle.js +10 -6
  40. package/build/async-require/loadBundle.js.map +1 -1
  41. package/build/effects.d.ts +0 -1
  42. package/build/effects.js +7 -10
  43. package/build/effects.js.map +1 -1
  44. package/build/error-overlay/Data/LogBoxData.d.ts.map +1 -1
  45. package/build/error-overlay/Data/LogBoxData.js +82 -33
  46. package/build/error-overlay/Data/LogBoxData.js.map +1 -1
  47. package/build/error-overlay/Data/LogBoxLog.js +29 -2
  48. package/build/error-overlay/Data/LogBoxLog.js.map +1 -1
  49. package/build/error-overlay/Data/LogBoxSymbolication.js +12 -4
  50. package/build/error-overlay/Data/LogBoxSymbolication.js.map +1 -1
  51. package/build/error-overlay/Data/LogContext.js +17 -9
  52. package/build/error-overlay/Data/LogContext.js.map +1 -1
  53. package/build/error-overlay/Data/parseLogBoxLog.d.ts.map +1 -1
  54. package/build/error-overlay/Data/parseLogBoxLog.js +20 -11
  55. package/build/error-overlay/Data/parseLogBoxLog.js.map +1 -1
  56. package/build/error-overlay/ErrorOverlay.d.ts.map +1 -1
  57. package/build/error-overlay/ErrorOverlay.js +73 -41
  58. package/build/error-overlay/ErrorOverlay.js.map +1 -1
  59. package/build/error-overlay/LogBox.js +3 -1
  60. package/build/error-overlay/LogBox.js.map +1 -1
  61. package/build/error-overlay/LogBox.web.d.ts.map +1 -1
  62. package/build/error-overlay/LogBox.web.js +4 -3
  63. package/build/error-overlay/LogBox.web.js.map +1 -1
  64. package/build/error-overlay/UI/AnsiHighlight.js +15 -8
  65. package/build/error-overlay/UI/AnsiHighlight.js.map +1 -1
  66. package/build/error-overlay/UI/LogBoxButton.js +35 -8
  67. package/build/error-overlay/UI/LogBoxButton.js.map +1 -1
  68. package/build/error-overlay/UI/LogBoxMessage.js +13 -6
  69. package/build/error-overlay/UI/LogBoxMessage.js.map +1 -1
  70. package/build/error-overlay/UI/LogBoxStyle.js +31 -14
  71. package/build/error-overlay/UI/LogBoxStyle.js.map +1 -1
  72. package/build/error-overlay/UI/constants.js +5 -2
  73. package/build/error-overlay/UI/constants.js.map +1 -1
  74. package/build/error-overlay/formatProjectFilePath.js +7 -2
  75. package/build/error-overlay/formatProjectFilePath.js.map +1 -1
  76. package/build/error-overlay/index.d.ts.map +1 -1
  77. package/build/error-overlay/index.js +19 -9
  78. package/build/error-overlay/index.js.map +1 -1
  79. package/build/error-overlay/modules/ExceptionsManager/index.js +8 -3
  80. package/build/error-overlay/modules/ExceptionsManager/index.js.map +1 -1
  81. package/build/error-overlay/modules/ExceptionsManager/index.native.js +7 -2
  82. package/build/error-overlay/modules/ExceptionsManager/index.native.js.map +1 -1
  83. package/build/error-overlay/modules/NativeLogBox/index.js +10 -5
  84. package/build/error-overlay/modules/NativeLogBox/index.js.map +1 -1
  85. package/build/error-overlay/modules/NativeLogBox/index.native.js +7 -2
  86. package/build/error-overlay/modules/NativeLogBox/index.native.js.map +1 -1
  87. package/build/error-overlay/modules/openFileInEditor/index.js +3 -1
  88. package/build/error-overlay/modules/openFileInEditor/index.js.map +1 -1
  89. package/build/error-overlay/modules/openFileInEditor/index.native.js +7 -2
  90. package/build/error-overlay/modules/openFileInEditor/index.native.js.map +1 -1
  91. package/build/error-overlay/modules/parseErrorStack/index.d.ts.map +1 -1
  92. package/build/error-overlay/modules/parseErrorStack/index.js +10 -7
  93. package/build/error-overlay/modules/parseErrorStack/index.js.map +1 -1
  94. package/build/error-overlay/modules/parseErrorStack/parseHermesStack.js +8 -2
  95. package/build/error-overlay/modules/parseErrorStack/parseHermesStack.js.map +1 -1
  96. package/build/error-overlay/modules/stringifySafe/index.js +6 -2
  97. package/build/error-overlay/modules/stringifySafe/index.js.map +1 -1
  98. package/build/error-overlay/modules/symbolicateStackTrace/index.js +3 -1
  99. package/build/error-overlay/modules/symbolicateStackTrace/index.js.map +1 -1
  100. package/build/error-overlay/modules/symbolicateStackTrace/index.native.js +7 -2
  101. package/build/error-overlay/modules/symbolicateStackTrace/index.native.js.map +1 -1
  102. package/build/error-overlay/overlay/LogBoxInspectorCodeFrame.d.ts.map +1 -1
  103. package/build/error-overlay/overlay/LogBoxInspectorCodeFrame.js +52 -22
  104. package/build/error-overlay/overlay/LogBoxInspectorCodeFrame.js.map +1 -1
  105. package/build/error-overlay/overlay/LogBoxInspectorFooter.js +48 -18
  106. package/build/error-overlay/overlay/LogBoxInspectorFooter.js.map +1 -1
  107. package/build/error-overlay/overlay/LogBoxInspectorHeader.js +53 -23
  108. package/build/error-overlay/overlay/LogBoxInspectorHeader.js.map +1 -1
  109. package/build/error-overlay/overlay/LogBoxInspectorMessageHeader.js +43 -13
  110. package/build/error-overlay/overlay/LogBoxInspectorMessageHeader.js.map +1 -1
  111. package/build/error-overlay/overlay/LogBoxInspectorSection.js +39 -9
  112. package/build/error-overlay/overlay/LogBoxInspectorSection.js.map +1 -1
  113. package/build/error-overlay/overlay/LogBoxInspectorSourceMapStatus.js +41 -14
  114. package/build/error-overlay/overlay/LogBoxInspectorSourceMapStatus.js.map +1 -1
  115. package/build/error-overlay/overlay/LogBoxInspectorStackFrame.js +44 -14
  116. package/build/error-overlay/overlay/LogBoxInspectorStackFrame.js.map +1 -1
  117. package/build/error-overlay/overlay/LogBoxInspectorStackFrames.d.ts.map +1 -1
  118. package/build/error-overlay/overlay/LogBoxInspectorStackFrames.js +55 -24
  119. package/build/error-overlay/overlay/LogBoxInspectorStackFrames.js.map +1 -1
  120. package/build/error-overlay/toast/ErrorToast.d.ts.map +1 -1
  121. package/build/error-overlay/toast/ErrorToast.js +48 -21
  122. package/build/error-overlay/toast/ErrorToast.js.map +1 -1
  123. package/build/error-overlay/toast/ErrorToastContainer.js +9 -3
  124. package/build/error-overlay/toast/ErrorToastContainer.js.map +1 -1
  125. package/build/error-overlay/toast/ErrorToastContainer.web.d.ts.map +1 -1
  126. package/build/error-overlay/toast/ErrorToastContainer.web.js +49 -21
  127. package/build/error-overlay/toast/ErrorToastContainer.web.js.map +1 -1
  128. package/build/error-overlay/toast/ErrorToastMessage.js +37 -7
  129. package/build/error-overlay/toast/ErrorToastMessage.js.map +1 -1
  130. package/build/error-overlay/useRejectionHandler.js +16 -9
  131. package/build/error-overlay/useRejectionHandler.js.map +1 -1
  132. package/build/getDevServer.d.ts.map +1 -1
  133. package/build/getDevServer.js +10 -9
  134. package/build/getDevServer.js.map +1 -1
  135. package/build/getDevServer.native.js +7 -2
  136. package/build/getDevServer.native.js.map +1 -1
  137. package/build/index.d.ts +7 -0
  138. package/build/index.d.ts.map +1 -1
  139. package/build/index.js +12 -8
  140. package/build/index.js.map +1 -1
  141. package/build/location/Location.js +7 -2
  142. package/build/location/Location.js.map +1 -1
  143. package/build/location/Location.native.js +12 -4
  144. package/build/location/Location.native.js.map +1 -1
  145. package/build/location/install.native.js +16 -11
  146. package/build/location/install.native.js.map +1 -1
  147. package/build/setupHMR.js +28 -26
  148. package/build/setupHMR.js.map +1 -1
  149. package/build/symbolicate.js +25 -4
  150. package/build/symbolicate.js.map +1 -1
  151. package/package.json +5 -2
  152. package/src/HMRClient.native.ts +3 -0
  153. package/src/HMRClient.ts +316 -0
  154. package/src/LoadingView.native.ts +3 -0
  155. package/src/LoadingView.ts +24 -0
  156. package/src/__mocks__/LoadingView.ts +4 -0
  157. package/src/async-require/buildAsyncRequire.ts +34 -0
  158. package/src/async-require/buildUrlForBundle.native.ts +28 -0
  159. package/src/async-require/buildUrlForBundle.ts +18 -0
  160. package/src/async-require/fetchAsync.native.ts +72 -0
  161. package/src/async-require/fetchAsync.ts +19 -0
  162. package/src/async-require/fetchThenEval.ts +1 -0
  163. package/src/async-require/fetchThenEval.web.ts +70 -0
  164. package/src/async-require/fetchThenEvalJs.ts +39 -0
  165. package/src/async-require/index.native.ts +15 -0
  166. package/src/async-require/index.ts +10 -0
  167. package/src/async-require/loadBundle.ts +46 -0
  168. package/src/effects.native.ts +0 -0
  169. package/src/effects.ts +11 -0
  170. package/src/error-overlay/Data/LogBoxData.tsx +438 -0
  171. package/src/error-overlay/Data/LogBoxLog.ts +221 -0
  172. package/src/error-overlay/Data/LogBoxSymbolication.tsx +64 -0
  173. package/src/error-overlay/Data/LogContext.tsx +41 -0
  174. package/src/error-overlay/Data/parseLogBoxLog.tsx +342 -0
  175. package/src/error-overlay/ErrorOverlay.tsx +191 -0
  176. package/src/error-overlay/LogBox.ts +51 -0
  177. package/src/error-overlay/LogBox.web.ts +174 -0
  178. package/src/error-overlay/UI/AnsiHighlight.tsx +96 -0
  179. package/src/error-overlay/UI/LogBoxButton.tsx +63 -0
  180. package/src/error-overlay/UI/LogBoxMessage.tsx +73 -0
  181. package/src/error-overlay/UI/LogBoxStyle.ts +64 -0
  182. package/src/error-overlay/UI/constants.ts +7 -0
  183. package/src/error-overlay/formatProjectFilePath.ts +38 -0
  184. package/src/error-overlay/index.tsx +34 -0
  185. package/src/error-overlay/modules/ExceptionsManager/index.native.ts +4 -0
  186. package/src/error-overlay/modules/ExceptionsManager/index.ts +82 -0
  187. package/src/error-overlay/modules/NativeLogBox/index.native.ts +3 -0
  188. package/src/error-overlay/modules/NativeLogBox/index.tsx +27 -0
  189. package/src/error-overlay/modules/openFileInEditor/index.native.ts +3 -0
  190. package/src/error-overlay/modules/openFileInEditor/index.ts +16 -0
  191. package/src/error-overlay/modules/parseErrorStack/index.ts +26 -0
  192. package/src/error-overlay/modules/parseErrorStack/parseHermesStack.ts +3 -0
  193. package/src/error-overlay/modules/stringifySafe/index.ts +115 -0
  194. package/src/error-overlay/modules/symbolicateStackTrace/index.native.ts +3 -0
  195. package/src/error-overlay/modules/symbolicateStackTrace/index.ts +39 -0
  196. package/src/error-overlay/overlay/LogBoxInspectorCodeFrame.tsx +102 -0
  197. package/src/error-overlay/overlay/LogBoxInspectorFooter.tsx +111 -0
  198. package/src/error-overlay/overlay/LogBoxInspectorHeader.tsx +167 -0
  199. package/src/error-overlay/overlay/LogBoxInspectorMessageHeader.tsx +116 -0
  200. package/src/error-overlay/overlay/LogBoxInspectorSection.tsx +52 -0
  201. package/src/error-overlay/overlay/LogBoxInspectorSourceMapStatus.tsx +125 -0
  202. package/src/error-overlay/overlay/LogBoxInspectorStackFrame.tsx +89 -0
  203. package/src/error-overlay/overlay/LogBoxInspectorStackFrames.tsx +201 -0
  204. package/src/error-overlay/toast/ErrorToast.tsx +167 -0
  205. package/src/error-overlay/toast/ErrorToastContainer.tsx +9 -0
  206. package/src/error-overlay/toast/ErrorToastContainer.web.tsx +92 -0
  207. package/src/error-overlay/toast/ErrorToastMessage.tsx +28 -0
  208. package/src/error-overlay/useRejectionHandler.ts +61 -0
  209. package/src/getDevServer.native.ts +3 -0
  210. package/src/getDevServer.ts +34 -0
  211. package/src/index.ts +12 -0
  212. package/src/location/Location.native.ts +201 -0
  213. package/src/location/Location.ts +3 -0
  214. package/src/location/install.native.ts +90 -0
  215. package/src/location/install.ts +0 -0
  216. package/src/messageSocket.ts +25 -0
  217. package/src/setupFastRefresh.ts +30 -0
  218. package/src/setupHMR.ts +28 -0
  219. package/src/symbolicate.ts +6 -0
@@ -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
+ }
@@ -0,0 +1,191 @@
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
+ import React, { useCallback, useEffect, useState } from 'react';
9
+ import { Keyboard, ScrollView, View, StyleSheet } from 'react-native';
10
+
11
+ import * as LogBoxData from './Data/LogBoxData';
12
+ import { LogBoxLog, StackType } from './Data/LogBoxLog';
13
+ import { useLogs, useSelectedLog } from './Data/LogContext';
14
+ import * as LogBoxStyle from './UI/LogBoxStyle';
15
+ import { LogBoxInspectorCodeFrame } from './overlay/LogBoxInspectorCodeFrame';
16
+ import { LogBoxInspectorFooter as ErrorOverlayFooter } from './overlay/LogBoxInspectorFooter';
17
+ import { LogBoxInspectorHeader as ErrorOverlayHeader } from './overlay/LogBoxInspectorHeader';
18
+ import { LogBoxInspectorMessageHeader } from './overlay/LogBoxInspectorMessageHeader';
19
+ import { LogBoxInspectorStackFrames } from './overlay/LogBoxInspectorStackFrames';
20
+
21
+ const HEADER_TITLE_MAP = {
22
+ warn: 'Console Warning',
23
+ error: 'Console Error',
24
+ fatal: 'Uncaught Error',
25
+ syntax: 'Syntax Error',
26
+ static: 'Static Rendering Error (Node.js)',
27
+ component: 'Render Error',
28
+ };
29
+
30
+ export function LogBoxInspectorContainer() {
31
+ const { selectedLogIndex, logs } = useLogs();
32
+ const log = logs[selectedLogIndex];
33
+ if (log == null) {
34
+ return null;
35
+ }
36
+ return <LogBoxInspector log={log} selectedLogIndex={selectedLogIndex} logs={logs} />;
37
+ }
38
+
39
+ export function LogBoxInspector({
40
+ log,
41
+ selectedLogIndex,
42
+ logs,
43
+ }: {
44
+ log: LogBoxLog;
45
+ selectedLogIndex: number;
46
+ logs: LogBoxLog[];
47
+ }) {
48
+ const onDismiss = useCallback((): void => {
49
+ // Here we handle the cases when the log is dismissed and it
50
+ // was either the last log, or when the current index
51
+ // is now outside the bounds of the log array.
52
+ const logsArray = Array.from(logs);
53
+ if (selectedLogIndex != null) {
54
+ if (logsArray.length - 1 <= 0) {
55
+ LogBoxData.setSelectedLog(-1);
56
+ } else if (selectedLogIndex >= logsArray.length - 1) {
57
+ LogBoxData.setSelectedLog(selectedLogIndex - 1);
58
+ }
59
+
60
+ LogBoxData.dismiss(logsArray[selectedLogIndex]);
61
+ }
62
+ }, [selectedLogIndex]);
63
+
64
+ const onMinimize = useCallback((): void => {
65
+ LogBoxData.setSelectedLog(-1);
66
+ }, []);
67
+
68
+ const onChangeSelectedIndex = useCallback((index: number): void => {
69
+ LogBoxData.setSelectedLog(index);
70
+ }, []);
71
+
72
+ useEffect(() => {
73
+ if (log) {
74
+ LogBoxData.symbolicateLogNow('stack', log);
75
+ LogBoxData.symbolicateLogNow('component', log);
76
+ }
77
+ }, [log]);
78
+
79
+ useEffect(() => {
80
+ // Optimistically symbolicate the last and next logs.
81
+ if (logs.length > 1) {
82
+ const selected = selectedLogIndex;
83
+ const lastIndex = logs.length - 1;
84
+ const prevIndex = selected - 1 < 0 ? lastIndex : selected - 1;
85
+ const nextIndex = selected + 1 > lastIndex ? 0 : selected + 1;
86
+ for (const type of ['component', 'stack'] as const) {
87
+ LogBoxData.symbolicateLogLazy(type, logs[prevIndex]);
88
+ LogBoxData.symbolicateLogLazy(type, logs[nextIndex]);
89
+ }
90
+ }
91
+ }, [logs, selectedLogIndex]);
92
+
93
+ useEffect(() => {
94
+ Keyboard.dismiss();
95
+ }, []);
96
+
97
+ const _handleRetry = useCallback(
98
+ (type: StackType) => {
99
+ LogBoxData.retrySymbolicateLogNow(type, log);
100
+ },
101
+ [log]
102
+ );
103
+
104
+ return (
105
+ <View style={styles.container}>
106
+ <ErrorOverlayHeader onSelectIndex={onChangeSelectedIndex} level={log.level} />
107
+ <ErrorOverlayBody onRetry={_handleRetry} />
108
+ <ErrorOverlayFooter onDismiss={onDismiss} onMinimize={onMinimize} />
109
+ </View>
110
+ );
111
+ }
112
+
113
+ export function ErrorOverlayBody({ onRetry }: { onRetry: (type: StackType) => void }) {
114
+ const log = useSelectedLog();
115
+ return <ErrorOverlayBodyContents log={log} onRetry={onRetry} />;
116
+ }
117
+
118
+ export function ErrorOverlayBodyContents({
119
+ log,
120
+ onRetry,
121
+ }: {
122
+ log: LogBoxLog;
123
+ onRetry: (type: StackType) => void;
124
+ }) {
125
+ const [collapsed, setCollapsed] = useState(true);
126
+
127
+ useEffect(() => {
128
+ setCollapsed(true);
129
+ }, [log]);
130
+
131
+ const headerTitle = HEADER_TITLE_MAP[log.isComponentError ? 'component' : log.level] ?? log.type;
132
+
133
+ const header = (
134
+ <LogBoxInspectorMessageHeader
135
+ collapsed={collapsed}
136
+ onPress={() => setCollapsed(!collapsed)}
137
+ message={log.message}
138
+ level={log.level}
139
+ title={headerTitle}
140
+ />
141
+ );
142
+
143
+ // Hide useless React stack.
144
+ const needsStack = !log.message.content.match(
145
+ /(Expected server HTML to contain a matching|Text content did not match\.)/
146
+ );
147
+
148
+ return (
149
+ <>
150
+ {collapsed && header}
151
+ <ScrollView style={styles.scrollBody}>
152
+ {!collapsed && header}
153
+
154
+ <LogBoxInspectorCodeFrame codeFrame={log.codeFrame} />
155
+ {needsStack && (
156
+ <LogBoxInspectorStackFrames
157
+ type="stack"
158
+ // eslint-disable-next-line react/jsx-no-bind
159
+ onRetry={onRetry.bind(onRetry, 'stack')}
160
+ />
161
+ )}
162
+ {!!log?.componentStack?.length && (
163
+ <LogBoxInspectorStackFrames
164
+ type="component"
165
+ // eslint-disable-next-line react/jsx-no-bind
166
+ onRetry={onRetry.bind(onRetry, 'component')}
167
+ />
168
+ )}
169
+ </ScrollView>
170
+ </>
171
+ );
172
+ }
173
+
174
+ const styles = StyleSheet.create({
175
+ scrollBody: {
176
+ backgroundColor: LogBoxStyle.getBackgroundColor(1),
177
+ flex: 1,
178
+ },
179
+ container: {
180
+ top: 0,
181
+ left: 0,
182
+ bottom: 0,
183
+ right: 0,
184
+ zIndex: 999,
185
+ flex: 1,
186
+ // @ts-expect-error: fixed is not in the RN types but it works on web
187
+ position: 'fixed',
188
+ },
189
+ });
190
+
191
+ export default LogBoxData.withSubscription(LogBoxInspectorContainer);
@@ -0,0 +1,51 @@
1
+ import { IgnorePattern, LogData } from './Data/LogBoxData';
2
+ import { ExtendedExceptionData } from './Data/parseLogBoxLog';
3
+
4
+ export { LogData, ExtendedExceptionData, IgnorePattern };
5
+
6
+ interface ILogBox {
7
+ install(): void;
8
+ uninstall(): void;
9
+ isInstalled(): boolean;
10
+ ignoreLogs(patterns: readonly IgnorePattern[]): void;
11
+ ignoreAllLogs(ignore?: boolean): void;
12
+ clearAllLogs(): void;
13
+ addLog(log: LogData): void;
14
+ addException(error: ExtendedExceptionData): void;
15
+ }
16
+
17
+ const LogBox: ILogBox = {
18
+ install(): void {
19
+ // Do nothing.
20
+ },
21
+
22
+ uninstall(): void {
23
+ // Do nothing.
24
+ },
25
+
26
+ isInstalled(): boolean {
27
+ return false;
28
+ },
29
+
30
+ ignoreLogs(patterns: readonly IgnorePattern[]): void {
31
+ // Do nothing.
32
+ },
33
+
34
+ ignoreAllLogs(value?: boolean): void {
35
+ // Do nothing.
36
+ },
37
+
38
+ clearAllLogs(): void {
39
+ // Do nothing.
40
+ },
41
+
42
+ addLog(log: LogData): void {
43
+ // Do nothing.
44
+ },
45
+
46
+ addException(ex: ExtendedExceptionData): void {
47
+ // Do nothing.
48
+ },
49
+ };
50
+
51
+ export default LogBox;