expo 55.0.0-canary-20251015-a6a1272 → 55.0.0-canary-20251023-4c86f95

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 (47) hide show
  1. package/Expo.podspec +1 -0
  2. package/android/build.gradle +15 -2
  3. package/android/src/main/java/expo/modules/ExpoReactHostFactory.kt +0 -1
  4. package/build/async-require/buildErrors.d.ts +5 -0
  5. package/build/async-require/buildErrors.d.ts.map +1 -0
  6. package/build/async-require/buildUrlForBundle.d.ts.map +1 -1
  7. package/build/async-require/getDevServer.d.ts.map +1 -1
  8. package/build/async-require/getFullBundlerUrl.d.ts +2 -0
  9. package/build/async-require/getFullBundlerUrl.d.ts.map +1 -0
  10. package/build/async-require/hmr.d.ts +8 -8
  11. package/build/async-require/hmr.d.ts.map +1 -1
  12. package/build/async-require/hmrUtils.d.ts +13 -0
  13. package/build/async-require/hmrUtils.d.ts.map +1 -0
  14. package/build/async-require/hmrUtils.native.d.ts +13 -0
  15. package/build/async-require/hmrUtils.native.d.ts.map +1 -0
  16. package/build/dom/dom-entry.d.ts.map +1 -1
  17. package/build/dom/dom-internal.types.d.ts +11 -0
  18. package/build/dom/dom-internal.types.d.ts.map +1 -0
  19. package/build/dom/dom.types.d.ts +5 -0
  20. package/build/dom/dom.types.d.ts.map +1 -1
  21. package/build/dom/internal.d.ts +1 -0
  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/bundledNativeModules.json +80 -80
  26. package/ios/AppDelegates/AppDelegatesLoaderDelegate.swift +2 -0
  27. package/ios/AppDelegates/ExpoReactNativeFactory.swift +2 -0
  28. package/ios/Fetch/ExpoFetchModule.swift +2 -2
  29. package/package.json +21 -20
  30. package/src/async-require/__tests__/buildUrlForBundle.test.web.ts +2 -2
  31. package/src/async-require/__tests__/loadBundle.test.web.ts +9 -5
  32. package/src/async-require/buildErrors.ts +14 -0
  33. package/src/async-require/buildUrlForBundle.ts +5 -13
  34. package/src/async-require/getDevServer.ts +3 -9
  35. package/src/async-require/getFullBundlerUrl.ts +13 -0
  36. package/src/async-require/hmr.ts +106 -104
  37. package/src/async-require/hmrUtils.native.ts +97 -0
  38. package/src/async-require/hmrUtils.ts +54 -0
  39. package/src/dom/dom-entry.tsx +13 -6
  40. package/src/dom/dom-internal.types.ts +9 -0
  41. package/src/dom/dom.types.ts +6 -0
  42. package/src/dom/internal.ts +2 -0
  43. package/src/dom/webview-wrapper.tsx +13 -5
  44. package/template.tgz +0 -0
  45. package/build/async-require/hmr.native.d.ts +0 -3
  46. package/build/async-require/hmr.native.d.ts.map +0 -1
  47. package/src/async-require/hmr.native.ts +0 -3
@@ -5,6 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import getDevServer from './getDevServer';
9
+
8
10
  /**
9
11
  * Given a path and some optional additional query parameters, create the dev server bundle URL.
10
12
  * @param bundlePath like `/foobar`
@@ -12,19 +14,9 @@
12
14
  * @returns a URL like "/foobar.bundle?platform=android&modulesOnly=true&runModule=false&runtimeBytecodeVersion=null"
13
15
  */
14
16
  export function buildUrlForBundle(bundlePath: string): string {
15
- if (bundlePath.match(/^https?:\/\//)) {
16
- return bundlePath;
17
- }
18
-
19
- if (
20
- typeof window !== 'undefined' &&
21
- // @ts-expect-error
22
- typeof window.$$EXPO_INITIAL_PROPS !== 'undefined'
23
- ) {
24
- // In a webview, you cannot read from an absolute path.
17
+ if (/^https?:\/\//.test(bundlePath)) {
25
18
  return bundlePath;
26
19
  }
27
- // NOTE(EvanBacon): This must come from the window origin (at least in dev mode).
28
- // Otherwise Metro will crash from attempting to load a bundle that doesn't exist.
29
- return '/' + bundlePath.replace(/^\/+/, '');
20
+ const { url: baseURL } = getDevServer();
21
+ return baseURL ? new URL(bundlePath, baseURL).toString() : `//${bundlePath.replace(/^\/+/, '')}`;
30
22
  }
@@ -1,3 +1,5 @@
1
+ import { getFullBundlerUrl } from './getFullBundlerUrl';
2
+
1
3
  const getDevServer = () => {
2
4
  // Disable for SSR
3
5
  if (typeof window === 'undefined') {
@@ -14,15 +16,7 @@ const getDevServer = () => {
14
16
 
15
17
  /** URL but ensures that platform query param is added. */
16
18
  get fullBundleUrl() {
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();
19
+ return getFullBundlerUrl();
26
20
  },
27
21
  url: location.origin + location.pathname,
28
22
  };
@@ -0,0 +1,13 @@
1
+ export function getFullBundlerUrl(): string {
2
+ const currentScript = document?.currentScript;
3
+ const bundleUrl = new URL(
4
+ currentScript && 'src' in currentScript ? currentScript.src : location.href,
5
+ location.href
6
+ );
7
+
8
+ if (!bundleUrl.searchParams.has('platform')) {
9
+ bundleUrl.searchParams.set('platform', process.env.EXPO_OS ?? 'web');
10
+ }
11
+
12
+ return bundleUrl.toString();
13
+ }
@@ -10,35 +10,24 @@
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';
14
13
 
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
- }
14
+ import {
15
+ getConnectionError,
16
+ getFullBundlerUrl,
17
+ handleCompileError,
18
+ hideLoading,
19
+ resetErrorOverlay,
20
+ showLoading,
21
+ } from './hmrUtils';
25
22
 
26
23
  const pendingEntryPoints: string[] = [];
27
24
 
28
25
  // @ts-expect-error: Account for multiple versions of pretty-format inside of a monorepo.
29
26
  const prettyFormatFunc = typeof prettyFormat === 'function' ? prettyFormat : prettyFormat.default;
30
27
 
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;
28
+ let hmrClient: MetroHMRClient | null = null;
40
29
  let hmrUnavailableReason: string | null = null;
41
- let currentCompileErrorMessage: string | null = null;
30
+ const buildErrorQueue = new Set<unknown>();
42
31
  let didConnect: boolean = false;
43
32
  const pendingLogs: [LogLevel, any[]][] = [];
44
33
 
@@ -53,14 +42,6 @@ type LogLevel =
53
42
  | 'groupEnd'
54
43
  | 'debug';
55
44
 
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
-
64
45
  function assert(foo: any, msg: string): asserts foo {
65
46
  if (!foo) throw new Error(msg);
66
47
  }
@@ -69,7 +50,7 @@ function assert(foo: any, msg: string): asserts foo {
69
50
  * HMR Client that receives from the server HMR updates and propagates them
70
51
  * runtime to reflects those changes.
71
52
  */
72
- const HMRClient: HMRClientNativeInterface = {
53
+ const HMRClient = {
73
54
  enable() {
74
55
  if (hmrUnavailableReason !== null) {
75
56
  // If HMR became unavailable while you weren't using it,
@@ -127,12 +108,19 @@ const HMRClient: HMRClientNativeInterface = {
127
108
  return;
128
109
  }
129
110
  try {
111
+ const webMetadata =
112
+ process.env.EXPO_OS === 'web'
113
+ ? {
114
+ platform: 'web',
115
+ mode: 'BRIDGE',
116
+ }
117
+ : undefined;
130
118
  hmrClient.send(
131
119
  JSON.stringify({
120
+ // TODO: Type this properly.
121
+ ...webMetadata,
132
122
  type: 'log',
133
123
  level,
134
- platform: 'web',
135
- mode: 'BRIDGE',
136
124
  data: data.map((item) =>
137
125
  typeof item === 'string'
138
126
  ? item
@@ -154,48 +142,63 @@ const HMRClient: HMRClientNativeInterface = {
154
142
 
155
143
  // Called once by the bridge on startup, even if Fast Refresh is off.
156
144
  // It creates the HMR client but doesn't actually set up the socket yet.
157
- setup({ isEnabled }: { isEnabled: boolean }) {
158
- assert(!hmrClient, 'Cannot initialize hmrClient twice');
159
-
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
- if (document?.currentScript && 'src' in document.currentScript) {
166
- return document.currentScript.src;
167
- }
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;
168
164
 
169
- const bundleUrl = new URL(location.href);
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`'
171
+ );
172
+ assert(bundleEntry, 'Missing required parameter `bundleEntry`');
173
+ assert(host, 'Missing required parameter `host`');
174
+ assert(!hmrClient, 'Cannot initialize hmrClient twice');
170
175
 
171
- bundleUrl.searchParams.set('platform', process.env.EXPO_OS ?? 'web');
176
+ serverHost = port !== null && port !== '' ? `${host}:${port}` : host;
177
+ serverScheme = scheme;
178
+ }
172
179
 
173
- return bundleUrl.toString();
174
- })();
180
+ const origin = `${serverScheme}://${serverHost}`;
181
+ const client = new MetroHMRClient(`${origin}/hot`);
182
+ hmrClient = client;
175
183
 
176
184
  pendingEntryPoints.push(
177
185
  // HMRServer understands regular bundle URLs, so prefer that in case
178
186
  // there are any important URL parameters we can't reconstruct from
179
187
  // `setup()`'s arguments.
180
- fullBundleUrl
188
+ getFullBundlerUrl({
189
+ serverScheme,
190
+ serverHost,
191
+ bundleEntry,
192
+ platform: typeof platformOrOptions === 'string' ? platformOrOptions : undefined,
193
+ })
181
194
  );
182
195
 
183
196
  client.on('connection-error', (e: Error) => {
184
- let error = `Cannot connect to Metro.
185
-
186
- Try the following to fix the issue:
187
- - Ensure the Metro dev server is running and available on the same network as this device`;
188
- error += `
189
-
190
- URL: ${window.location.host}
191
-
192
- Error: ${e.message}`;
193
-
194
- setHMRUnavailableReason(error);
197
+ setHMRUnavailableReason(getConnectionError(serverHost, e));
195
198
  });
196
199
 
197
200
  client.on('update-start', ({ isInitialUpdate }: { isInitialUpdate?: boolean }) => {
198
- currentCompileErrorMessage = null;
201
+ buildErrorQueue.clear();
199
202
  didConnect = true;
200
203
 
201
204
  if (client.isEnabled() && !isInitialUpdate) {
@@ -205,10 +208,7 @@ const HMRClient: HMRClientNativeInterface = {
205
208
 
206
209
  client.on('update', ({ isInitialUpdate }: { isInitialUpdate?: boolean }) => {
207
210
  if (client.isEnabled() && !isInitialUpdate) {
208
- dismissRedbox();
209
- // @ts-expect-error
210
- globalThis.__expo_dev_resetErrors?.();
211
- // LogBox.clearAllLogs();
211
+ resetErrorOverlay();
212
212
  }
213
213
  });
214
214
 
@@ -216,22 +216,7 @@ const HMRClient: HMRClientNativeInterface = {
216
216
  hideLoading();
217
217
  });
218
218
 
219
- client.on('error', (data: { type: string; message: string }) => {
220
- hideLoading();
221
-
222
- if (data.type === 'GraphNotFoundError') {
223
- client.close();
224
- setHMRUnavailableReason('Metro has restarted since the last edit. Reload to reconnect.');
225
- } else if (data.type === 'RevisionNotFoundError') {
226
- client.close();
227
- setHMRUnavailableReason('Metro and the client are out of sync. Reload to reconnect.');
228
- } else {
229
- currentCompileErrorMessage = `${data.type} ${data.message}`;
230
- if (client.isEnabled()) {
231
- showCompileError();
232
- }
233
- }
234
- });
219
+ client.on('error', (data) => this._onMetroError(data));
235
220
 
236
221
  client.on('close', (closeEvent: { code: number; reason: string }) => {
237
222
  hideLoading();
@@ -242,19 +227,19 @@ const HMRClient: HMRClientNativeInterface = {
242
227
  closeEvent == null ||
243
228
  closeEvent.code === 1000 ||
244
229
  closeEvent.code === 1005 ||
230
+ closeEvent.code === 1006 ||
245
231
  closeEvent.code == null;
246
232
 
247
233
  setHMRUnavailableReason(
248
234
  `${
249
235
  isNormalOrUnsetCloseReason
250
- ? 'Disconnected from Metro.'
251
- : `Disconnected from Metro (${closeEvent.code}: "${closeEvent.reason}").`
236
+ ? 'Disconnected from Expo CLI.'
237
+ : `Disconnected from Expo CLI (${closeEvent.code}: "${closeEvent.reason}").`
252
238
  }
253
239
 
254
240
  To reconnect:
255
- - Ensure that Metro is running and available on the same network
256
- - Reload this app (will trigger further help if Metro cannot be connected to)
257
- `
241
+ - Start the dev server with: npx expo
242
+ - Reload the ${process.env.EXPO_OS === 'web' ? 'page' : 'app'}`
258
243
  );
259
244
  });
260
245
 
@@ -267,6 +252,37 @@ To reconnect:
267
252
  registerBundleEntryPoints(hmrClient);
268
253
  flushEarlyLogs();
269
254
  },
255
+
256
+ // Related Metro error's formatting
257
+ // https://github.com/facebook/metro/blob/34bb8913ec4b5b02690b39d2246599faf094f721/packages/metro/src/lib/formatBundlingError.js#L36
258
+ _onMetroError(error: unknown) {
259
+ if (!hmrClient) {
260
+ return;
261
+ }
262
+
263
+ assert(typeof error === 'object' && error != null, 'Expected data to be an object');
264
+
265
+ hideLoading();
266
+
267
+ if ('type' in error) {
268
+ if (error.type === 'GraphNotFoundError') {
269
+ hmrClient.close();
270
+ setHMRUnavailableReason('Expo CLI has restarted since the last edit. Reload to reconnect.');
271
+ return;
272
+ } else if (error.type === 'RevisionNotFoundError') {
273
+ hmrClient.close();
274
+ setHMRUnavailableReason(
275
+ `Expo CLI and the ${process.env.EXPO_OS} client are out of sync. Reload to reconnect.`
276
+ );
277
+ return;
278
+ }
279
+ }
280
+
281
+ buildErrorQueue.add(error);
282
+ if (hmrClient.isEnabled()) {
283
+ showCompileError();
284
+ }
285
+ },
270
286
  };
271
287
 
272
288
  function setHMRUnavailableReason(reason: string) {
@@ -286,7 +302,7 @@ function setHMRUnavailableReason(reason: string) {
286
302
  }
287
303
  }
288
304
 
289
- function registerBundleEntryPoints(client: HMRClientType | null) {
305
+ function registerBundleEntryPoints(client: MetroHMRClient | null) {
290
306
  if (hmrUnavailableReason != null) {
291
307
  // "Bundle Splitting – Metro disconnected"
292
308
  window.location.reload();
@@ -314,28 +330,14 @@ function flushEarlyLogs() {
314
330
  }
315
331
  }
316
332
 
317
- function dismissRedbox() {
318
- // TODO(EvanBacon): Error overlay for web.
319
- }
320
-
321
333
  function showCompileError() {
322
- if (currentCompileErrorMessage === null) {
334
+ if (buildErrorQueue.size === 0) {
323
335
  return;
324
336
  }
325
337
 
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;
338
+ const cause: any = buildErrorQueue.values().next().value;
339
+ buildErrorQueue.clear();
340
+ handleCompileError(cause);
339
341
  }
340
342
 
341
343
  export default HMRClient;
@@ -0,0 +1,97 @@
1
+ // Based on https://github.com/facebook/react-native/blob/9ab95dd2b5746e8323ad1d65591d5a4ec7718790/packages/react-native/Libraries/Utilities/HMRClient.js
2
+
3
+ // @ts-expect-error missing types
4
+ import getDevServer from 'react-native/Libraries/Core/Devtools/getDevServer';
5
+ import LogBox from 'react-native/Libraries/LogBox/LogBox';
6
+ // @ts-expect-error missing types
7
+ import NativeRedBox from 'react-native/Libraries/NativeModules/specs/NativeRedBox';
8
+ import DevSettings from 'react-native/Libraries/Utilities/DevSettings';
9
+
10
+ import { HMRMetroBuildError } from './buildErrors';
11
+
12
+ export function showLoading(message: string, type: 'load' | 'refresh') {
13
+ const DevLoadingView = require('react-native/Libraries/Utilities/DevLoadingView').default;
14
+ DevLoadingView.showMessage(message, type);
15
+ }
16
+
17
+ export function hideLoading() {
18
+ const DevLoadingView = require('react-native/Libraries/Utilities/DevLoadingView').default;
19
+ DevLoadingView.hide();
20
+ }
21
+
22
+ export function resetErrorOverlay() {
23
+ dismissRedbox();
24
+ // @ts-expect-error clearAllLogs exists, but ts types are missing
25
+ LogBox.clearAllLogs();
26
+ }
27
+
28
+ export function reload() {
29
+ // @ts-expect-error missing types
30
+ DevSettings.reload('Bundle Splitting – Metro disconnected');
31
+ }
32
+
33
+ export function getFullBundlerUrl({
34
+ serverScheme,
35
+ serverHost,
36
+ bundleEntry,
37
+ platform,
38
+ }: {
39
+ serverScheme: string;
40
+ serverHost: string;
41
+ bundleEntry: string;
42
+ platform: string;
43
+ }): string {
44
+ return (
45
+ getDevServer().fullBundleUrl ??
46
+ `${serverScheme}://${serverHost}/hot?bundleEntry=${bundleEntry}&platform=${platform}`
47
+ );
48
+ }
49
+
50
+ export function getConnectionError(serverHost: string, e: Error): string {
51
+ let error = `Cannot connect to Expo CLI.
52
+
53
+ Try the following to fix the issue:
54
+ - Ensure that Expo dev server is running and available on the same network`;
55
+
56
+ if (process.env.EXPO_OS === 'android') {
57
+ error += `
58
+ - Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices
59
+ - If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device`;
60
+ }
61
+
62
+ error += `
63
+
64
+ URL: ${serverHost}
65
+
66
+ Error: ${e.message}`;
67
+ return error;
68
+ }
69
+
70
+ function dismissRedbox() {
71
+ if (process.env.EXPO_OS === 'ios' && NativeRedBox != null && NativeRedBox.dismiss != null) {
72
+ NativeRedBox.dismiss();
73
+ } else {
74
+ const NativeExceptionsManager =
75
+ require('react-native/Libraries/Core/NativeExceptionsManager').default;
76
+ NativeExceptionsManager &&
77
+ NativeExceptionsManager.dismissRedbox &&
78
+ NativeExceptionsManager.dismissRedbox();
79
+ }
80
+ }
81
+
82
+ export function handleCompileError(cause: any) {
83
+ if (cause === null) {
84
+ return;
85
+ }
86
+
87
+ // Even if there is already a redbox, syntax errors are more important.
88
+ // Otherwise you risk seeing a stale runtime error while a syntax error is more recent.
89
+ dismissRedbox();
90
+
91
+ const LogBox = require('react-native/Libraries/LogBox/LogBox').default;
92
+ // The error is passed thru LogBox APIs directly to the parsing function.
93
+ // Won't log the error in devtools console
94
+ // (using throw would mangle the error message and print with ANSI
95
+ // because throw on native is processed as console.error)
96
+ LogBox.addException(new HMRMetroBuildError(cause.message, cause.type, cause.cause));
97
+ }
@@ -0,0 +1,54 @@
1
+ import { DeviceEventEmitter } from 'react-native';
2
+
3
+ import { HMRMetroBuildError } from './buildErrors';
4
+ import { getFullBundlerUrl as getFullBundlerUrlHelper } from './getFullBundlerUrl';
5
+
6
+ export function getFullBundlerUrl(_: {
7
+ serverScheme?: string;
8
+ serverHost?: string;
9
+ bundleEntry?: string;
10
+ platform?: string;
11
+ }): string {
12
+ return getFullBundlerUrlHelper();
13
+ }
14
+
15
+ export function showLoading(message: string, _type: 'load' | 'refresh') {
16
+ // Ensure events are sent so custom Fast Refresh views are shown.
17
+ DeviceEventEmitter.emit('devLoadingView:showMessage', {
18
+ message,
19
+ });
20
+ }
21
+
22
+ export function hideLoading() {
23
+ DeviceEventEmitter.emit('devLoadingView:hide', {});
24
+ }
25
+
26
+ export function resetErrorOverlay() {
27
+ // @ts-expect-error
28
+ globalThis.__expo_dev_resetErrors?.();
29
+ }
30
+
31
+ export function reload() {
32
+ // "Bundle Splitting – Metro disconnected"
33
+ window.location.reload();
34
+ }
35
+
36
+ export function getConnectionError(serverHost: string, e: Error): string {
37
+ return `
38
+ Cannot connect to Expo CLI.
39
+
40
+ Try the following to fix the issue:
41
+ - Ensure the Expo dev server is running and available on the same network as this device
42
+
43
+ URL: ${serverHost}
44
+
45
+ Error: ${e.message}
46
+ `.trim();
47
+ }
48
+
49
+ export function handleCompileError(cause: any) {
50
+ if (!cause) {
51
+ return;
52
+ }
53
+ throw new HMRMetroBuildError(cause.message, cause.type, cause.cause);
54
+ }
@@ -1,7 +1,6 @@
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';
5
4
  import React from 'react';
6
5
 
7
6
  import { JSONValue } from './dom.types';
@@ -15,6 +14,7 @@ interface MarshalledProps {
15
14
  }
16
15
 
17
16
  interface WindowType {
17
+ $$EXPO_DOM_HOST_OS?: string;
18
18
  $$EXPO_INITIAL_PROPS?: MarshalledProps;
19
19
  }
20
20
 
@@ -65,6 +65,13 @@ 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
+
68
75
  function DOMComponentRoot(props: Record<string, unknown>) {
69
76
  // Props listeners
70
77
  const [marshalledProps, setProps] = React.useState(() => {
@@ -102,12 +109,12 @@ export function registerDOMComponent(AppModule: any) {
102
109
  }
103
110
 
104
111
  try {
112
+ if (process.env.NODE_ENV !== 'production') {
113
+ require('@expo/log-box/lib').setupLogBox();
114
+ }
115
+
105
116
  React.startTransition(() => {
106
- if (process.env.NODE_ENV !== 'production') {
107
- registerRootComponent(withErrorOverlay(DOMComponentRoot));
108
- } else {
109
- registerRootComponent(DOMComponentRoot);
110
- }
117
+ registerRootComponent(DOMComponentRoot);
111
118
  });
112
119
  } catch (e) {
113
120
  const error = convertError(e);
@@ -0,0 +1,9 @@
1
+ import type { DOMProps } from './dom.types';
2
+
3
+ export interface DOMPropsInternal extends DOMProps {
4
+ /**
5
+ * Allows dynamically redirecting a component to a different source, for example prebuilt version.
6
+ * @internal
7
+ */
8
+ sourceOverride?: { uri: string };
9
+ }
@@ -36,4 +36,10 @@ 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;
39
45
  }
@@ -1,5 +1,7 @@
1
1
  export { default as WebView } from './webview-wrapper';
2
2
 
3
+ export * from './dom-internal.types';
4
+
3
5
  // Skip all dom-only functions to give 'undefined is not a function' errors.
4
6
  export const registerDOMComponent: undefined | typeof import('./dom-entry').registerDOMComponent =
5
7
  undefined;
@@ -1,9 +1,14 @@
1
1
  // A webview without babel to test faster.
2
+ //
3
+ // Keep in sync with ExpoLogBox native webview wrappers.
4
+ // Android https://github.com/expo/expo/blob/main/packages/%40expo/log-box/android/src/main/expo/modules/logbox/ExpoLogBoxWebViewWrapper.kt
5
+ // iOS https://github.com/expo/expo/blob/main/packages/%40expo/log-box/ios/ExpoLogBoxWebViewWrapper.swift
2
6
  import React from 'react';
3
7
  import { AppState } from 'react-native';
4
8
 
5
9
  import { getBaseURL } from './base';
6
- import type { BridgeMessage, DOMProps, WebViewProps, WebViewRef } from './dom.types';
10
+ import { DOMPropsInternal } from './dom-internal.types';
11
+ import type { BridgeMessage, WebViewProps, WebViewRef } from './dom.types';
7
12
  import { _emitGlobalEvent } from './global-events';
8
13
  import {
9
14
  getInjectBodySizeObserverScript,
@@ -22,14 +27,15 @@ type RawWebViewProps = React.ComponentProps<Exclude<typeof ExpoDomWebView, undef
22
27
 
23
28
  interface Props {
24
29
  children?: any;
25
- dom?: DOMProps;
30
+ dom?: DOMPropsInternal;
26
31
  filePath: string;
27
32
  ref: React.Ref<object>;
28
33
  [propName: string]: unknown;
29
34
  }
30
35
 
31
36
  const RawWebView = React.forwardRef<object, Props>((props, ref) => {
32
- const { children, dom, filePath, ref: _ref, ...marshalProps } = props as Props;
37
+ const { children, dom: domProps, filePath, ref: _ref, ...marshalProps } = props as Props;
38
+ const { overrideUri, ...dom } = domProps || {};
33
39
  if (__DEV__) {
34
40
  if (children !== undefined) {
35
41
  throw new Error(
@@ -66,7 +72,7 @@ const RawWebView = React.forwardRef<object, Props>((props, ref) => {
66
72
  const webView = resolveWebView(dom?.useExpoDOMWebView ?? false);
67
73
  const webviewRef = React.useRef<WebViewRef>(null);
68
74
  const domImperativeHandlePropsRef = React.useRef<string[]>([]);
69
- const source = { uri: `${getBaseURL()}/${filePath}` };
75
+ const source = { uri: overrideUri ?? `${getBaseURL()}/${filePath}` };
70
76
  const [containerStyle, setContainerStyle] = React.useState<WebViewProps['containerStyle']>(null);
71
77
 
72
78
  const { debugZeroHeightStyle, debugOnLayout } = useDebugZeroHeight(dom);
@@ -129,10 +135,12 @@ const RawWebView = React.forwardRef<object, Props>((props, ref) => {
129
135
  subscription.remove();
130
136
  });
131
137
  },
132
- ...dom,
138
+ ...domProps,
133
139
  containerStyle: [containerStyle, debugZeroHeightStyle, dom?.containerStyle],
134
140
  onLayout: __DEV__ ? debugOnLayout : dom?.onLayout,
135
141
  injectedJavaScriptBeforeContentLoaded: [
142
+ // Inject the top-most OS for the DOM component to read.
143
+ `window.$$EXPO_DOM_HOST_OS = ${JSON.stringify(process.env.EXPO_OS)};true;`,
136
144
  // On first mount, inject `$$EXPO_INITIAL_PROPS` with the initial props.
137
145
  `window.$$EXPO_INITIAL_PROPS = ${JSON.stringify(smartActions)};true;`,
138
146
  dom?.matchContents ? getInjectBodySizeObserverScript() : null,
package/template.tgz CHANGED
Binary file