expo 55.0.0-canary-20260119-70f7c28 → 55.0.0-canary-20260119-17896bf

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 (72) hide show
  1. package/Expo.podspec +0 -1
  2. package/android/build.gradle +5 -5
  3. package/android/src/main/java/expo/modules/ExpoReactHostFactory.kt +32 -67
  4. package/android/src/main/java/expo/modules/ReactActivityDelegateWrapper.kt +9 -6
  5. package/android/src/main/java/expo/modules/ReactNativeHostWrapper.kt +51 -0
  6. package/android/src/main/java/expo/modules/ReactNativeHostWrapperBase.kt +107 -0
  7. package/android/src/main/java/expo/modules/fetch/ExpoFetchModule.kt +3 -4
  8. package/android/src/main/java/expo/modules/fetch/NativeResponse.kt +3 -3
  9. package/android/src/main/java/expo/modules/fetch/ResponseSink.kt +3 -7
  10. package/android/src/test/java/expo/modules/ReactActivityDelegateWrapperDelayLoadTest.kt +3 -2
  11. package/build/Expo.d.ts +1 -1
  12. package/build/Expo.d.ts.map +1 -1
  13. package/build/async-require/getDevServer.d.ts.map +1 -1
  14. package/build/async-require/hmr.d.ts +8 -8
  15. package/build/async-require/hmr.d.ts.map +1 -1
  16. package/build/async-require/hmr.native.d.ts +3 -0
  17. package/build/async-require/hmr.native.d.ts.map +1 -0
  18. package/build/dom/dom-entry.d.ts.map +1 -1
  19. package/build/dom/dom.types.d.ts +0 -5
  20. package/build/dom/dom.types.d.ts.map +1 -1
  21. package/build/dom/internal.d.ts +0 -1
  22. package/build/dom/internal.d.ts.map +1 -1
  23. package/build/dom/webview-wrapper.d.ts +2 -2
  24. package/build/dom/webview-wrapper.d.ts.map +1 -1
  25. package/build/hooks/useEvent.d.ts +2 -2
  26. package/bundledNativeModules.json +101 -99
  27. package/ios/AppDelegates/AppDelegatesLoaderDelegate.swift +0 -2
  28. package/ios/AppDelegates/EXAppDelegateWrapper.h +30 -0
  29. package/ios/AppDelegates/EXAppDelegateWrapper.mm +112 -0
  30. package/ios/AppDelegates/EXReactRootViewFactory.h +1 -8
  31. package/ios/AppDelegates/EXReactRootViewFactory.mm +0 -26
  32. package/ios/AppDelegates/ExpoAppDelegate.swift +52 -13
  33. package/ios/AppDelegates/ExpoReactNativeFactory.swift +1 -22
  34. package/ios/Expo.h +1 -2
  35. package/ios/Fetch/ExpoFetchModule.swift +2 -2
  36. package/package.json +23 -29
  37. package/src/Expo.fx.tsx +25 -1
  38. package/src/Expo.fx.web.tsx +2 -2
  39. package/src/Expo.ts +0 -3
  40. package/src/__tests__/__fbBatchedBridgeConfig-test.ts +3 -7
  41. package/src/async-require/asyncRequireModule.ts +2 -2
  42. package/src/async-require/getDevServer.ts +9 -3
  43. package/src/async-require/hmr.native.ts +3 -0
  44. package/src/async-require/hmr.ts +103 -118
  45. package/src/async-require/index.ts +1 -1
  46. package/src/async-require/setupFastRefresh.ts +2 -3
  47. package/src/dom/dom-entry.tsx +8 -15
  48. package/src/dom/dom.types.ts +0 -6
  49. package/src/dom/internal.ts +0 -2
  50. package/src/dom/webview-wrapper.tsx +6 -14
  51. package/src/hooks/useEvent.ts +2 -2
  52. package/template.tgz +0 -0
  53. package/types/react-native-web.d.ts +1 -1
  54. package/build/async-require/buildErrors.d.ts +0 -5
  55. package/build/async-require/buildErrors.d.ts.map +0 -1
  56. package/build/async-require/getFullBundlerUrl.d.ts +0 -2
  57. package/build/async-require/getFullBundlerUrl.d.ts.map +0 -1
  58. package/build/async-require/hmrUtils.d.ts +0 -13
  59. package/build/async-require/hmrUtils.d.ts.map +0 -1
  60. package/build/async-require/hmrUtils.native.d.ts +0 -13
  61. package/build/async-require/hmrUtils.native.d.ts.map +0 -1
  62. package/build/dom/dom-internal.types.d.ts +0 -11
  63. package/build/dom/dom-internal.types.d.ts.map +0 -1
  64. package/ios/AppDelegates/ExpoReactNativeFactory.h +0 -12
  65. package/ios/AppDelegates/ExpoReactNativeFactory.mm +0 -45
  66. package/local-build-cache-provider.d.ts +0 -1
  67. package/local-build-cache-provider.js +0 -1
  68. package/src/async-require/buildErrors.ts +0 -14
  69. package/src/async-require/getFullBundlerUrl.ts +0 -13
  70. package/src/async-require/hmrUtils.native.ts +0 -97
  71. package/src/async-require/hmrUtils.ts +0 -54
  72. package/src/dom/dom-internal.types.ts +0 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo",
3
- "version": "55.0.0-canary-20260119-70f7c28",
3
+ "version": "55.0.0-canary-20260119-17896bf",
4
4
  "description": "The Expo SDK",
5
5
  "main": "src/Expo.ts",
6
6
  "module": "src/Expo.ts",
@@ -40,8 +40,6 @@
40
40
  "fetch.d.ts",
41
41
  "fingerprint.js",
42
42
  "fingerprint.d.ts",
43
- "local-build-cache-provider.js",
44
- "local-build-cache-provider.d.ts",
45
43
  "react-native.config.js",
46
44
  "requiresExtraSetup.json",
47
45
  "tsconfig.base.json",
@@ -77,44 +75,40 @@
77
75
  "homepage": "https://github.com/expo/expo/tree/main/packages/expo",
78
76
  "dependencies": {
79
77
  "@babel/runtime": "^7.20.0",
80
- "@expo/cli": "55.0.0-canary-20260119-70f7c28",
81
- "@expo/config": "12.0.14-canary-20260119-70f7c28",
82
- "@expo/config-plugins": "54.1.0-canary-20260119-70f7c28",
83
- "@expo/devtools": "0.1.9-canary-20260119-70f7c28",
84
- "@expo/fingerprint": "0.15.5-canary-20260119-70f7c28",
85
- "@expo/local-build-cache-provider": "0.0.1-canary-20260119-70f7c28",
78
+ "@expo/cli": "54.1.0-canary-20260119-17896bf",
79
+ "@expo/config": "12.0.14-canary-20260119-17896bf",
80
+ "@expo/config-plugins": "54.0.5-canary-20260119-17896bf",
81
+ "@expo/devtools": "0.1.9-canary-20260119-17896bf",
82
+ "@expo/fingerprint": "0.15.5-canary-20260119-17896bf",
86
83
  "@expo/metro": "~54.2.0",
87
- "@expo/metro-config": "54.1.0-canary-20260119-70f7c28",
88
- "@expo/vector-icons": "^15.0.2",
89
- "@expo/log-box": "0.0.13-canary-20260119-70f7c28",
84
+ "@expo/metro-config": "54.0.14-canary-20260119-17896bf",
85
+ "@expo/vector-icons": "^15.0.3",
90
86
  "@ungap/structured-clone": "^1.3.0",
91
- "babel-preset-expo": "54.1.0-canary-20260119-70f7c28",
92
- "expo-asset": "12.0.13-canary-20260119-70f7c28",
93
- "expo-constants": "18.1.0-canary-20260119-70f7c28",
94
- "expo-file-system": "19.0.22-canary-20260119-70f7c28",
95
- "expo-font": "14.1.0-canary-20260119-70f7c28",
96
- "expo-keep-awake": "15.0.9-canary-20260119-70f7c28",
97
- "expo-modules-autolinking": "3.1.0-canary-20260119-70f7c28",
98
- "expo-modules-core": "4.0.0-canary-20260119-70f7c28",
87
+ "babel-preset-expo": "54.0.10-canary-20260119-17896bf",
88
+ "expo-asset": "12.0.13-canary-20260119-17896bf",
89
+ "expo-constants": "18.0.14-canary-20260119-17896bf",
90
+ "expo-file-system": "19.0.22-canary-20260119-17896bf",
91
+ "expo-font": "14.0.11-canary-20260119-17896bf",
92
+ "expo-keep-awake": "15.0.9-canary-20260119-17896bf",
93
+ "expo-modules-autolinking": "3.0.25-canary-20260119-17896bf",
94
+ "expo-modules-core": "3.0.30-canary-20260119-17896bf",
99
95
  "pretty-format": "^29.7.0",
100
96
  "react-refresh": "^0.14.2",
101
97
  "whatwg-url-without-unicode": "8.0.0-3"
102
98
  },
103
99
  "devDependencies": {
104
- "@expo/dom-webview": "0.2.9-canary-20260119-70f7c28",
105
- "@expo/metro-runtime": "6.2.0-canary-20260119-70f7c28",
106
100
  "@types/node": "^22.14.0",
107
- "@types/react": "~19.2.0",
101
+ "@types/react": "~19.1.10",
108
102
  "@types/react-test-renderer": "~19.1.0",
109
- "expo-module-scripts": "5.1.0-canary-20260119-70f7c28",
110
- "react": "19.2.0",
111
- "react-dom": "19.2.0",
112
- "react-native": "0.83.1",
103
+ "expo-module-scripts": "5.0.9-canary-20260119-17896bf",
104
+ "react": "19.1.0",
105
+ "react-dom": "19.1.0",
106
+ "react-native": "0.81.5",
113
107
  "web-streams-polyfill": "^3.3.2"
114
108
  },
115
109
  "peerDependencies": {
116
- "@expo/dom-webview": "0.2.9-canary-20260119-70f7c28",
117
- "@expo/metro-runtime": "6.2.0-canary-20260119-70f7c28",
110
+ "@expo/dom-webview": "0.2.9-canary-20260119-17896bf",
111
+ "@expo/metro-runtime": "6.1.3-canary-20260119-17896bf",
118
112
  "react": "*",
119
113
  "react-native": "*",
120
114
  "react-native-webview": "*"
package/src/Expo.fx.tsx CHANGED
@@ -4,7 +4,8 @@ import './async-require';
4
4
  import 'expo-asset';
5
5
  import 'expo/virtual/rsc';
6
6
 
7
- import { AppRegistry, NativeModules, LogBox } from 'react-native';
7
+ import Constants from 'expo-constants';
8
+ import { AppRegistry, NativeModules, LogBox, Platform } from 'react-native';
8
9
 
9
10
  import { isRunningInExpoGo } from './environment/ExpoGo';
10
11
  import { AppEntryNotFound } from './errors/AppEntryNotFound';
@@ -25,6 +26,29 @@ if (isRunningInExpoGo()) {
25
26
  ErrorUtils.setGlobalHandler(createErrorHandler(globalHandler));
26
27
  }
27
28
 
29
+ // Warn if the New Architecture is not explicitly enabled in the app config and we are running in Expo Go.
30
+ // This could be problematic because you will be developing your app with the New Architecture enabled and
31
+ // but your builds will have the New Architecture disabled.
32
+ if (__DEV__ && isRunningInExpoGo() && process.env.NODE_ENV === 'development') {
33
+ (['android', 'ios'] as const).forEach((platform) => {
34
+ const newArchPlatformConfig = Constants.expoConfig?.[platform]?.newArchEnabled;
35
+ const newArchRootConfig = Constants.expoConfig?.newArchEnabled;
36
+
37
+ const isNewArchExplicitlyDisabled =
38
+ newArchPlatformConfig === false ||
39
+ (newArchPlatformConfig === undefined && newArchRootConfig === false);
40
+
41
+ if (Platform.OS === platform && isNewArchExplicitlyDisabled) {
42
+ // Wrap it in rAF to show the warning after the React Native DevTools message
43
+ requestAnimationFrame(() => {
44
+ console.warn(
45
+ `🚨 React Native's New Architecture is always enabled in Expo Go, but it is explicitly disabled in your project's app config. This may lead to unexpected behavior when creating a production or development build. Remove "newArchEnabled": false from your app.json.\nLearn more: https://docs.expo.dev/guides/new-architecture/`
46
+ );
47
+ });
48
+ }
49
+ });
50
+ }
51
+
28
52
  // Disable the "Open debugger to view warnings" React Native DevTools warning in
29
53
  // Expo Go and expo-dev-client, because launching the debugger from there will not
30
54
  // get the correct JS target.
@@ -9,9 +9,9 @@ import 'expo/virtual/rsc';
9
9
  if (__DEV__) {
10
10
  if (
11
11
  // Skip mocking if someone is shimming this value out.
12
- !('__fbBatchedBridgeConfig' in globalThis)
12
+ !('__fbBatchedBridgeConfig' in global)
13
13
  ) {
14
- Object.defineProperty(globalThis, '__fbBatchedBridgeConfig', {
14
+ Object.defineProperty(global, '__fbBatchedBridgeConfig', {
15
15
  get() {
16
16
  throw new Error(
17
17
  "Your web project is importing a module from 'react-native' instead of 'react-native-web'. Learn more: https://expo.fyi/fb-batched-bridge-config-web"
package/src/Expo.ts CHANGED
@@ -18,9 +18,6 @@ export {
18
18
  requireNativeViewManager as requireNativeView,
19
19
  registerWebModule,
20
20
  reloadAppAsync,
21
-
22
- // Worklets
23
- installOnUIRuntime,
24
21
  } from 'expo-modules-core';
25
22
 
26
23
  export type {
@@ -6,18 +6,14 @@ jest.mock('../async-require/getDevServer');
6
6
 
7
7
  if (Platform.OS === 'web') {
8
8
  it('provides a helpful error message on web', () => {
9
- const error =
10
- /Your web project is importing a module from 'react-native' instead of 'react-native-web'/;
11
9
  // @ts-ignore
12
- expect(() => global.__fbBatchedBridgeConfig).toThrow(error);
13
- // @ts-ignore
14
- expect(() => globalThis.__fbBatchedBridgeConfig).toThrow(error);
10
+ expect(() => global.__fbBatchedBridgeConfig).toThrow(
11
+ /Your web project is importing a module from 'react-native' instead of 'react-native-web'/
12
+ );
15
13
  });
16
14
  } else {
17
15
  it(`does not change the functionality of __fbBatchedBridgeConfig on native`, () => {
18
16
  // @ts-ignore
19
17
  expect(() => global.__fbBatchedBridgeConfig).not.toThrow();
20
- // @ts-ignore
21
- expect(() => globalThis.__fbBatchedBridgeConfig).not.toThrow();
22
18
  });
23
19
  }
@@ -20,8 +20,8 @@ type DependencyMapPaths = { [moduleID: number | string]: unknown } | null;
20
20
  declare let __METRO_GLOBAL_PREFIX__: string;
21
21
 
22
22
  function maybeLoadBundle(moduleID: number, paths: DependencyMapPaths): void | Promise<void> {
23
- const loadBundle: (bundlePath: unknown) => Promise<void> = (globalThis as any)[
24
- `${__METRO_GLOBAL_PREFIX__ ?? ''}__loadBundleAsync`
23
+ const loadBundle: (bundlePath: unknown) => Promise<void> = (global as any)[
24
+ `${__METRO_GLOBAL_PREFIX__}__loadBundleAsync`
25
25
  ];
26
26
 
27
27
  if (loadBundle != null) {
@@ -1,5 +1,3 @@
1
- import { getFullBundlerUrl } from './getFullBundlerUrl';
2
-
3
1
  const getDevServer = () => {
4
2
  // Disable for SSR
5
3
  if (typeof window === 'undefined') {
@@ -16,7 +14,15 @@ const getDevServer = () => {
16
14
 
17
15
  /** URL but ensures that platform query param is added. */
18
16
  get fullBundleUrl() {
19
- return getFullBundlerUrl();
17
+ if (document?.currentScript && 'src' in document.currentScript) {
18
+ return document.currentScript.src;
19
+ }
20
+
21
+ const bundleUrl = new URL(location.href);
22
+
23
+ bundleUrl.searchParams.set('platform', 'web');
24
+
25
+ return bundleUrl.toString();
20
26
  },
21
27
  url: location.origin + location.pathname,
22
28
  };
@@ -0,0 +1,3 @@
1
+ const HMRClient = require('react-native/Libraries/Utilities/HMRClient').default;
2
+
3
+ export default HMRClient;
@@ -10,24 +10,35 @@
10
10
  */
11
11
  import MetroHMRClient from '@expo/metro/metro-runtime/modules/HMRClient';
12
12
  import prettyFormat, { plugins } from 'pretty-format';
13
+ import { DeviceEventEmitter } from 'react-native';
13
14
 
14
- import {
15
- getConnectionError,
16
- getFullBundlerUrl,
17
- handleCompileError,
18
- hideLoading,
19
- resetErrorOverlay,
20
- showLoading,
21
- } from './hmrUtils';
15
+ // Ensure events are sent so custom Fast Refresh views are shown.
16
+ function showLoading(message: string, _type: 'load' | 'refresh') {
17
+ DeviceEventEmitter.emit('devLoadingView:showMessage', {
18
+ message,
19
+ });
20
+ }
21
+
22
+ function hideLoading() {
23
+ DeviceEventEmitter.emit('devLoadingView:hide', {});
24
+ }
22
25
 
23
26
  const pendingEntryPoints: string[] = [];
24
27
 
25
28
  // @ts-expect-error: Account for multiple versions of pretty-format inside of a monorepo.
26
29
  const prettyFormatFunc = typeof prettyFormat === 'function' ? prettyFormat : prettyFormat.default;
27
30
 
28
- let hmrClient: MetroHMRClient | null = null;
31
+ type HMRClientType = {
32
+ send: (msg: string) => void;
33
+ isEnabled: () => boolean;
34
+ disable: () => void;
35
+ enable: () => void;
36
+ hasPendingUpdates: () => boolean;
37
+ };
38
+
39
+ let hmrClient: HMRClientType | null = null;
29
40
  let hmrUnavailableReason: string | null = null;
30
- const buildErrorQueue = new Set<unknown>();
41
+ let currentCompileErrorMessage: string | null = null;
31
42
  let didConnect: boolean = false;
32
43
  const pendingLogs: [LogLevel, any[]][] = [];
33
44
 
@@ -42,6 +53,14 @@ type LogLevel =
42
53
  | 'groupEnd'
43
54
  | 'debug';
44
55
 
56
+ export type HMRClientNativeInterface = {
57
+ enable(): void;
58
+ disable(): void;
59
+ registerBundle(requestUrl: string): void;
60
+ log(level: LogLevel, data: any[]): void;
61
+ setup(props: { isEnabled: boolean }): void;
62
+ };
63
+
45
64
  function assert(foo: any, msg: string): asserts foo {
46
65
  if (!foo) throw new Error(msg);
47
66
  }
@@ -50,7 +69,7 @@ function assert(foo: any, msg: string): asserts foo {
50
69
  * HMR Client that receives from the server HMR updates and propagates them
51
70
  * runtime to reflects those changes.
52
71
  */
53
- const HMRClient = {
72
+ const HMRClient: HMRClientNativeInterface = {
54
73
  enable() {
55
74
  if (hmrUnavailableReason !== null) {
56
75
  // If HMR became unavailable while you weren't using it,
@@ -108,19 +127,12 @@ const HMRClient = {
108
127
  return;
109
128
  }
110
129
  try {
111
- const webMetadata =
112
- process.env.EXPO_OS === 'web'
113
- ? {
114
- platform: 'web',
115
- mode: 'BRIDGE',
116
- }
117
- : undefined;
118
130
  hmrClient.send(
119
131
  JSON.stringify({
120
- // TODO: Type this properly.
121
- ...webMetadata,
122
132
  type: 'log',
123
133
  level,
134
+ platform: 'web',
135
+ mode: 'BRIDGE',
124
136
  data: data.map((item) =>
125
137
  typeof item === 'string'
126
138
  ? item
@@ -142,63 +154,50 @@ const HMRClient = {
142
154
 
143
155
  // Called once by the bridge on startup, even if Fast Refresh is off.
144
156
  // It creates the HMR client but doesn't actually set up the socket yet.
145
- setup(
146
- platformOrOptions: string | { isEnabled: boolean },
147
- bundleEntry?: string,
148
- host?: string,
149
- port?: number | string,
150
- isEnabledOrUndefined?: boolean,
151
- scheme: string = 'http'
152
- ) {
153
- let isEnabled = !!isEnabledOrUndefined;
154
- let serverScheme: string;
155
- let serverHost: string;
156
-
157
- if (process.env.EXPO_OS === 'web') {
158
- assert(
159
- platformOrOptions && typeof platformOrOptions === 'object',
160
- 'Expected platformOrOptions to be an options object on web.'
161
- );
162
- assert(!hmrClient, 'Cannot initialize hmrClient twice');
163
- isEnabled = platformOrOptions.isEnabled;
157
+ setup({ isEnabled }: { isEnabled: boolean }) {
158
+ assert(!hmrClient, 'Cannot initialize hmrClient twice');
164
159
 
165
- serverScheme = window.location.protocol === 'https:' ? 'wss' : 'ws';
166
- serverHost = window.location.host;
167
- } else {
168
- assert(
169
- platformOrOptions && typeof platformOrOptions === 'string',
170
- 'Missing required parameter `platform`'
160
+ const serverScheme = window.location.protocol === 'https:' ? 'wss' : 'ws';
161
+ const client = new MetroHMRClient(`${serverScheme}://${window.location.host}/hot`);
162
+ hmrClient = client;
163
+
164
+ const fullBundleUrl = (() => {
165
+ const currentScript = document?.currentScript;
166
+ const bundleUrl = new URL(
167
+ currentScript && 'src' in currentScript ? currentScript.src : location.href,
168
+ location.href
171
169
  );
172
- assert(bundleEntry, 'Missing required parameter `bundleEntry`');
173
- assert(host, 'Missing required parameter `host`');
174
- assert(!hmrClient, 'Cannot initialize hmrClient twice');
175
170
 
176
- serverHost = port !== null && port !== '' ? `${host}:${port}` : host;
177
- serverScheme = scheme;
178
- }
171
+ if (!bundleUrl.searchParams.has('platform')) {
172
+ bundleUrl.searchParams.set('platform', process.env.EXPO_OS ?? 'web');
173
+ }
179
174
 
180
- const origin = `${serverScheme}://${serverHost}`;
181
- const client = new MetroHMRClient(`${origin}/hot`);
182
- hmrClient = client;
175
+ return bundleUrl.toString();
176
+ })();
183
177
 
184
178
  pendingEntryPoints.push(
185
179
  // HMRServer understands regular bundle URLs, so prefer that in case
186
180
  // there are any important URL parameters we can't reconstruct from
187
181
  // `setup()`'s arguments.
188
- getFullBundlerUrl({
189
- serverScheme,
190
- serverHost,
191
- bundleEntry,
192
- platform: typeof platformOrOptions === 'string' ? platformOrOptions : undefined,
193
- })
182
+ fullBundleUrl
194
183
  );
195
184
 
196
185
  client.on('connection-error', (e: Error) => {
197
- setHMRUnavailableReason(getConnectionError(serverHost, e));
186
+ let error = `Cannot connect to Metro.
187
+
188
+ Try the following to fix the issue:
189
+ - Ensure the Metro dev server is running and available on the same network as this device`;
190
+ error += `
191
+
192
+ URL: ${window.location.host}
193
+
194
+ Error: ${e.message}`;
195
+
196
+ setHMRUnavailableReason(error);
198
197
  });
199
198
 
200
199
  client.on('update-start', ({ isInitialUpdate }: { isInitialUpdate?: boolean }) => {
201
- buildErrorQueue.clear();
200
+ currentCompileErrorMessage = null;
202
201
  didConnect = true;
203
202
 
204
203
  if (client.isEnabled() && !isInitialUpdate) {
@@ -206,32 +205,35 @@ const HMRClient = {
206
205
  }
207
206
  });
208
207
 
209
- client.on(
210
- 'update',
211
- ({
212
- isInitialUpdate,
213
- added,
214
- modified,
215
- deleted,
216
- }: {
217
- isInitialUpdate?: boolean;
218
- added: unknown[];
219
- modified: unknown[];
220
- deleted: unknown[];
221
- }) => {
222
- // NOTE(@krystofwoldrich): I don't know why sometimes empty updates are sent. But they should not reset the overlay.
223
- const isEmpty = added.length === 0 && modified.length === 0 && deleted.length === 0;
224
- if (client.isEnabled() && !isInitialUpdate && !isEmpty) {
225
- resetErrorOverlay();
226
- }
208
+ client.on('update', ({ isInitialUpdate }: { isInitialUpdate?: boolean }) => {
209
+ if (client.isEnabled() && !isInitialUpdate) {
210
+ dismissRedbox();
211
+ // @ts-expect-error
212
+ globalThis.__expo_dev_resetErrors?.();
213
+ // LogBox.clearAllLogs();
227
214
  }
228
- );
215
+ });
229
216
 
230
217
  client.on('update-done', () => {
231
218
  hideLoading();
232
219
  });
233
220
 
234
- client.on('error', (data) => this._onMetroError(data));
221
+ client.on('error', (data: { type: string; message: string }) => {
222
+ hideLoading();
223
+
224
+ if (data.type === 'GraphNotFoundError') {
225
+ client.close();
226
+ setHMRUnavailableReason('Metro has restarted since the last edit. Reload to reconnect.');
227
+ } else if (data.type === 'RevisionNotFoundError') {
228
+ client.close();
229
+ setHMRUnavailableReason('Metro and the client are out of sync. Reload to reconnect.');
230
+ } else {
231
+ currentCompileErrorMessage = `${data.type} ${data.message}`;
232
+ if (client.isEnabled()) {
233
+ showCompileError();
234
+ }
235
+ }
236
+ });
235
237
 
236
238
  client.on('close', (closeEvent?: { code: number; reason: string }) => {
237
239
  hideLoading();
@@ -260,37 +262,6 @@ const HMRClient = {
260
262
  registerBundleEntryPoints(hmrClient);
261
263
  flushEarlyLogs();
262
264
  },
263
-
264
- // Related Metro error's formatting
265
- // https://github.com/facebook/metro/blob/34bb8913ec4b5b02690b39d2246599faf094f721/packages/metro/src/lib/formatBundlingError.js#L36
266
- _onMetroError(error: unknown) {
267
- if (!hmrClient) {
268
- return;
269
- }
270
-
271
- assert(typeof error === 'object' && error != null, 'Expected data to be an object');
272
-
273
- hideLoading();
274
-
275
- if ('type' in error) {
276
- if (error.type === 'GraphNotFoundError') {
277
- hmrClient.close();
278
- setHMRUnavailableReason('Expo CLI has restarted since the last edit. Reload to reconnect.');
279
- return;
280
- } else if (error.type === 'RevisionNotFoundError') {
281
- hmrClient.close();
282
- setHMRUnavailableReason(
283
- `Expo CLI and the ${process.env.EXPO_OS} client are out of sync. Reload to reconnect.`
284
- );
285
- return;
286
- }
287
- }
288
-
289
- buildErrorQueue.add(error);
290
- if (hmrClient.isEnabled()) {
291
- showCompileError();
292
- }
293
- },
294
265
  };
295
266
 
296
267
  function setHMRUnavailableReason(reason: string) {
@@ -315,7 +286,7 @@ function setHMRUnavailableReason(reason: string) {
315
286
  }
316
287
  }
317
288
 
318
- function registerBundleEntryPoints(client: MetroHMRClient | null) {
289
+ function registerBundleEntryPoints(client: HMRClientType | null) {
319
290
  if (hmrUnavailableReason != null) {
320
291
  // "Bundle Splitting – Metro disconnected"
321
292
  window.location.reload();
@@ -343,14 +314,28 @@ function flushEarlyLogs() {
343
314
  }
344
315
  }
345
316
 
317
+ function dismissRedbox() {
318
+ // TODO(EvanBacon): Error overlay for web.
319
+ }
320
+
346
321
  function showCompileError() {
347
- if (buildErrorQueue.size === 0) {
322
+ if (currentCompileErrorMessage === null) {
348
323
  return;
349
324
  }
350
325
 
351
- const cause: any = buildErrorQueue.values().next().value;
352
- buildErrorQueue.clear();
353
- handleCompileError(cause);
326
+ // Even if there is already a redbox, syntax errors are more important.
327
+ // Otherwise you risk seeing a stale runtime error while a syntax error is more recent.
328
+ dismissRedbox();
329
+
330
+ const message = currentCompileErrorMessage;
331
+ currentCompileErrorMessage = null;
332
+
333
+ const error = new Error(message);
334
+ // Symbolicating compile errors is wasted effort
335
+ // because the stack trace is meaningless:
336
+ // @ts-expect-error
337
+ error.preventSymbolication = true;
338
+ throw error;
354
339
  }
355
340
 
356
341
  export default HMRClient;
@@ -7,4 +7,4 @@
7
7
  import { buildAsyncRequire } from './buildAsyncRequire';
8
8
 
9
9
  // @ts-ignore: ignore the global which may not always be defined in jest environments.
10
- globalThis[`${globalThis.__METRO_GLOBAL_PREFIX__ ?? ''}__loadBundleAsync`] = buildAsyncRequire();
10
+ global[`${global.__METRO_GLOBAL_PREFIX__ ?? ''}__loadBundleAsync`] = buildAsyncRequire();
@@ -1,7 +1,7 @@
1
1
  // This needs to run before the renderer initializes.
2
2
 
3
3
  const ReactRefreshRuntime = require('react-refresh/runtime');
4
- ReactRefreshRuntime.injectIntoGlobalHook(globalThis);
4
+ ReactRefreshRuntime.injectIntoGlobalHook(global);
5
5
 
6
6
  const Refresh = {
7
7
  performFullRefresh() {
@@ -27,5 +27,4 @@ const Refresh = {
27
27
 
28
28
  // The metro require polyfill can not have dependencies (applies for all polyfills).
29
29
  // Expose `Refresh` by assigning it to global to make it available in the polyfill.
30
- // @ts-ignore: ignore the global which may not always be defined in jest environments.
31
- globalThis[(globalThis.__METRO_GLOBAL_PREFIX__ ?? '') + '__ReactRefresh'] = Refresh;
30
+ global[(global.__METRO_GLOBAL_PREFIX__ || '') + '__ReactRefresh'] = Refresh;
@@ -1,6 +1,7 @@
1
1
  // Entry file for a DOM Component.
2
2
  import '@expo/metro-runtime';
3
3
 
4
+ import { withErrorOverlay } from '@expo/metro-runtime/error-overlay';
4
5
  import React from 'react';
5
6
 
6
7
  import { JSONValue } from './dom.types';
@@ -8,13 +9,12 @@ import { addEventListener, getActionsObject } from './marshal';
8
9
  import registerRootComponent from '../launch/registerRootComponent';
9
10
 
10
11
  interface MarshalledProps {
11
- names: string[];
12
+ name: string[];
12
13
  props: Record<string, JSONValue>;
13
14
  [key: string]: undefined | JSONValue;
14
15
  }
15
16
 
16
17
  interface WindowType {
17
- $$EXPO_DOM_HOST_OS?: string;
18
18
  $$EXPO_INITIAL_PROPS?: MarshalledProps;
19
19
  }
20
20
 
@@ -65,13 +65,6 @@ function convertError(error: any) {
65
65
  }
66
66
 
67
67
  export function registerDOMComponent(AppModule: any) {
68
- if (typeof window.$$EXPO_DOM_HOST_OS === 'undefined') {
69
- throw new Error(
70
- 'Top OS ($$EXPO_DOM_HOST_OS) is not defined. This is a bug in the DOM Component runtime.'
71
- );
72
- }
73
- process.env.EXPO_DOM_HOST_OS = window.$$EXPO_DOM_HOST_OS;
74
-
75
68
  function DOMComponentRoot(props: Record<string, unknown>) {
76
69
  // Props listeners
77
70
  const [marshalledProps, setProps] = React.useState(() => {
@@ -99,7 +92,7 @@ export function registerDOMComponent(AppModule: any) {
99
92
  // Create a named map { [name: string]: ProxyFunction }
100
93
  // TODO(@kitten): Unclear how this is typed or shaped
101
94
  return Object.fromEntries(
102
- marshalledProps.names.map((key: string) => {
95
+ (marshalledProps.names as string[]).map((key: string) => {
103
96
  return [key, ACTIONS[key]];
104
97
  })
105
98
  );
@@ -109,12 +102,12 @@ export function registerDOMComponent(AppModule: any) {
109
102
  }
110
103
 
111
104
  try {
112
- if (process.env.NODE_ENV !== 'production') {
113
- require('@expo/log-box/lib').setupLogBox();
114
- }
115
-
116
105
  React.startTransition(() => {
117
- registerRootComponent(DOMComponentRoot);
106
+ if (process.env.NODE_ENV !== 'production') {
107
+ registerRootComponent(withErrorOverlay(DOMComponentRoot));
108
+ } else {
109
+ registerRootComponent(DOMComponentRoot);
110
+ }
118
111
  });
119
112
  } catch (e) {
120
113
  const error = convertError(e);
@@ -36,10 +36,4 @@ export interface DOMProps extends Omit<RNWebViewProps, 'source'> {
36
36
  * @default false
37
37
  */
38
38
  useExpoDOMWebView?: boolean;
39
-
40
- /**
41
- * Allows dynamically redirecting a component to a different source, for example a prebuilt version.
42
- * @internal
43
- */
44
- overrideUri?: string;
45
39
  }
@@ -1,7 +1,5 @@
1
1
  export { default as WebView } from './webview-wrapper';
2
2
 
3
- export * from './dom-internal.types';
4
-
5
3
  // Skip all dom-only functions to give 'undefined is not a function' errors.
6
4
  export const registerDOMComponent: undefined | typeof import('./dom-entry').registerDOMComponent =
7
5
  undefined;