1import { registerRootComponent } from 'expo'; 2import React from 'react'; 3import { Platform, View } from 'react-native'; 4 5import { SplashScreen, _internal_preventAutoHideAsync } from './views/Splash'; 6 7function isBaseObject(obj: any) { 8 if (Object.prototype.toString.call(obj) !== '[object Object]') { 9 return false; 10 } 11 const proto = Object.getPrototypeOf(obj); 12 if (proto === null) { 13 return true; 14 } 15 return proto === Object.prototype; 16} 17 18function isErrorShaped(error: any): error is Error { 19 return ( 20 error && 21 typeof error === 'object' && 22 typeof error.name === 'string' && 23 typeof error.message === 'string' 24 ); 25} 26 27/** 28 * After we throw this error, any number of tools could handle it. 29 * This check ensures the error is always in a reason state before surfacing it to the runtime. 30 */ 31function convertError(error: any) { 32 if (isErrorShaped(error)) { 33 return error; 34 } 35 36 if (process.env.NODE_ENV === 'development') { 37 if (error == null) { 38 return new Error('A null/undefined error was thrown.'); 39 } 40 } 41 42 if (isBaseObject(error)) { 43 return new Error(JSON.stringify(error)); 44 } 45 46 return new Error(String(error)); 47} 48 49/** 50 * Register and mount the root component using the predefined rendering 51 * method. This function ensures the Splash Screen and errors are handled correctly. 52 */ 53export function renderRootComponent(Component: React.ComponentType<any>) { 54 try { 55 // This must be delayed so the user has a chance to call it first. 56 setTimeout(() => { 57 _internal_preventAutoHideAsync(); 58 }); 59 60 if (process.env.NODE_ENV !== 'production') { 61 const { withErrorOverlay } = 62 require('@expo/metro-runtime/error-overlay') as typeof import('@expo/metro-runtime/error-overlay'); 63 registerRootComponent(withErrorOverlay(Component)); 64 } else { 65 registerRootComponent(Component); 66 } 67 } catch (e) { 68 // Hide the splash screen if there was an error so the user can see it. 69 SplashScreen.hideAsync(); 70 71 const error = convertError(e); 72 // Prevent the app from throwing confusing: 73 // ERROR Invariant Violation: "main" has not been registered. This can happen if: 74 // * Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project. 75 // * A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called. 76 registerRootComponent(() => <View />); 77 78 // Console is pretty useless on native, on web you get interactive stack traces. 79 if (Platform.OS === 'web') { 80 console.error(error); 81 console.error(`A runtime error has occurred while rendering the root component.`); 82 } 83 84 // Give React a tick to render before throwing. 85 setTimeout(() => { 86 throw error; 87 }); 88 89 // TODO: Render a production-only error screen. 90 } 91} 92