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