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.
- package/Expo.podspec +1 -0
- package/android/build.gradle +15 -2
- package/android/src/main/java/expo/modules/ExpoReactHostFactory.kt +0 -1
- package/build/async-require/buildErrors.d.ts +5 -0
- package/build/async-require/buildErrors.d.ts.map +1 -0
- package/build/async-require/buildUrlForBundle.d.ts.map +1 -1
- package/build/async-require/getDevServer.d.ts.map +1 -1
- package/build/async-require/getFullBundlerUrl.d.ts +2 -0
- package/build/async-require/getFullBundlerUrl.d.ts.map +1 -0
- package/build/async-require/hmr.d.ts +8 -8
- package/build/async-require/hmr.d.ts.map +1 -1
- package/build/async-require/hmrUtils.d.ts +13 -0
- package/build/async-require/hmrUtils.d.ts.map +1 -0
- package/build/async-require/hmrUtils.native.d.ts +13 -0
- package/build/async-require/hmrUtils.native.d.ts.map +1 -0
- package/build/dom/dom-entry.d.ts.map +1 -1
- package/build/dom/dom-internal.types.d.ts +11 -0
- package/build/dom/dom-internal.types.d.ts.map +1 -0
- package/build/dom/dom.types.d.ts +5 -0
- package/build/dom/dom.types.d.ts.map +1 -1
- package/build/dom/internal.d.ts +1 -0
- package/build/dom/internal.d.ts.map +1 -1
- package/build/dom/webview-wrapper.d.ts +2 -2
- package/build/dom/webview-wrapper.d.ts.map +1 -1
- package/bundledNativeModules.json +80 -80
- package/ios/AppDelegates/AppDelegatesLoaderDelegate.swift +2 -0
- package/ios/AppDelegates/ExpoReactNativeFactory.swift +2 -0
- package/ios/Fetch/ExpoFetchModule.swift +2 -2
- package/package.json +21 -20
- package/src/async-require/__tests__/buildUrlForBundle.test.web.ts +2 -2
- package/src/async-require/__tests__/loadBundle.test.web.ts +9 -5
- package/src/async-require/buildErrors.ts +14 -0
- package/src/async-require/buildUrlForBundle.ts +5 -13
- package/src/async-require/getDevServer.ts +3 -9
- package/src/async-require/getFullBundlerUrl.ts +13 -0
- package/src/async-require/hmr.ts +106 -104
- package/src/async-require/hmrUtils.native.ts +97 -0
- package/src/async-require/hmrUtils.ts +54 -0
- package/src/dom/dom-entry.tsx +13 -6
- package/src/dom/dom-internal.types.ts +9 -0
- package/src/dom/dom.types.ts +6 -0
- package/src/dom/internal.ts +2 -0
- package/src/dom/webview-wrapper.tsx +13 -5
- package/template.tgz +0 -0
- package/build/async-require/hmr.native.d.ts +0 -3
- package/build/async-require/hmr.native.d.ts.map +0 -1
- 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 (
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/src/async-require/hmr.ts
CHANGED
|
@@ -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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
+
serverHost = port !== null && port !== '' ? `${host}:${port}` : host;
|
|
177
|
+
serverScheme = scheme;
|
|
178
|
+
}
|
|
172
179
|
|
|
173
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
251
|
-
: `Disconnected from
|
|
236
|
+
? 'Disconnected from Expo CLI.'
|
|
237
|
+
: `Disconnected from Expo CLI (${closeEvent.code}: "${closeEvent.reason}").`
|
|
252
238
|
}
|
|
253
239
|
|
|
254
240
|
To reconnect:
|
|
255
|
-
-
|
|
256
|
-
- Reload
|
|
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:
|
|
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 (
|
|
334
|
+
if (buildErrorQueue.size === 0) {
|
|
323
335
|
return;
|
|
324
336
|
}
|
|
325
337
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
+
}
|
package/src/dom/dom-entry.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/src/dom/dom.types.ts
CHANGED
|
@@ -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
|
}
|
package/src/dom/internal.ts
CHANGED
|
@@ -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
|
|
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?:
|
|
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
|
-
...
|
|
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
|