Skip to content

Instantly share code, notes, and snippets.

@rodneyrehm
Created December 23, 2020 16:21
Show Gist options
  • Save rodneyrehm/dedc06800c0239f9ed7c2f9b7c423544 to your computer and use it in GitHub Desktop.
Save rodneyrehm/dedc06800c0239f9ed7c2f9b7c423544 to your computer and use it in GitHub Desktop.
Patching expo for expo-constants, expo-linking and expo-font to work in Bare Workflow

Patching Expo to work in all workflows (Bare and Managed)

{
  "expo": {
    "entryPoint": "index.js"
  }
}
/*
* In the SDK38 Bare Workflow `expo-constants` does not expose all values in all situations.
*
* Situations:
* running in Expo Client with JS from `expo start`
* running in Standalone with JS from `react-native start`
* running in Standalone with JS bundled
*
* Documentation:
* https://docs.expo.io/guides/environment-variables/
* https://docs.expo.io/versions/v38.0.0/sdk/constants/
* https://docs.expo.io/versions/v38.0.0/sdk/updates/
*
* Issues:
* https://github.com/expo/expo/issues/7721
* https://forums.expo.io/t/error-after-ejecting-to-bare-workflow-null-is-not-an-object-error-evaluating-expoconstants-default-manifest-releasechannel/28497/3
*/
import Constants, { AppOwnership } from 'expo-constants'
import { AppManifest } from 'expo-constants/build/Constants.types'
import * as Updates from 'expo-updates'
import manifestJson from '$src/../app.json'
const manifest: AppManifest = (
// in Expo Client with JS from `expo start`
// in Standalone with JS bundled
Constants.manifest ||
// in Standalone with JS from `react-native start`
{ ...manifestJson.expo, bundleUrl: '' }
)
const appOwnership: AppOwnership = (
// in Expo Client with JS from `expo start`
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsappownership
Constants.appOwnership ||
// in Standalone with JS from `react-native start`
AppOwnership.Standalone
)
const releaseChannel = (
// running in Standalone with JS bundled
Updates.releaseChannel ||
// everywhere else (the default value of `Updates.releaseChannel`)
'default'
)
const expoVersion = (
// in Expo Client with JS from `expo start`
Constants.expoVersion ||
// everywhere else
null
)
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsdevicename
const deviceName = Constants.deviceName
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsdeviceyearclass
const deviceYearClass = Constants.deviceYearClass
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsinstallationid
const installationId = Constants.installationId
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsisdevice
const isDevice = Constants.isDevice
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsnativeappversion
const nativeAppVersion = String(Constants.nativeAppVersion)
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsnativebuildversion
const nativeBuildVersion = String(Constants.nativeBuildVersion)
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsplatform
const platform = Constants.platform
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantssessionid
const sessionId = Constants.sessionId
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantsstatusbarheight
const statusBarHeight = Constants.statusBarHeight
// available from expo-constants in every situation
// https://docs.expo.io/versions/v38.0.0/sdk/constants/#constantssystemfonts
const systemFonts = Constants.systemFonts
export {
appOwnership,
deviceName,
deviceYearClass,
expoVersion,
installationId,
isDevice,
manifest,
nativeAppVersion,
nativeBuildVersion,
platform,
releaseChannel,
sessionId,
statusBarHeight,
systemFonts,
}
/* eslint-disable no-import-assign */
/*
* WARNING!
*
* This file contains ugly hack-arounds because expo-constants
* does not yet know its place in the Bare Workflow.
* Eventually all of this should become obsolete
*/
import { Platform } from 'react-native'
import { Asset } from 'expo-asset'
import Constants, { ExecutionEnvironment } from 'expo-constants'
import ExpoFontLoader from 'expo-font/build/ExpoFontLoader'
import { FontResource } from 'expo-font/build/Font.types'
import * as FontLoader from 'expo-font/build/FontLoader'
import * as Linking from 'expo-linking/build/Linking'
import { CodedError } from '@unimodules/core'
import { appOwnership, manifest } from './expo-constants'
import Config from '$src/native/react-native-config'
// Patch Constants for expo-linking
// In order for the patch to take effect this file has to be executed before expo-linking is loaded
// https://github.com/expo/expo/blob/7592d4d/packages/expo-linking/src/Linking.ts#L13
if (Constants.appOwnership === null) {
Constants.appOwnership = appOwnership
Constants.manifest = manifest
}
// Patch expo-linking to construct URL when not in expo client
// https://github.com/expo/expo/blob/7592d4d/packages/expo-linking/src/Linking.ts#L114
if (Constants.executionEnvironment === ExecutionEnvironment.Bare) {
const _makeUrl = Linking.makeUrl
// @ts-expect-error
Linking.makeUrl = (path = '', queryParams?: Linking.QueryParams) => {
Constants.executionEnvironment = ExecutionEnvironment.StoreClient
const result = _makeUrl(path, queryParams)
Constants.executionEnvironment = ExecutionEnvironment.Bare
return result
}
}
// Patch expo-font to load fonts when in `npm run rn:ios`
// https://github.com/expo/expo/blob/1e8f40c/packages/expo-font/src/FontLoader.ts#L10
if (Config.BUILD_CONFIG === 'debug' && Platform.OS === 'ios') {
// @ts-expect-error
FontLoader.fontFamilyNeedsScoping = () => false
// @ts-expect-error
FontLoader.getNativeFontName = (name: string) => name
// @ts-expect-error
FontLoader.loadSingleFontAsync = async (
name: string,
input: Asset | FontResource,
): Promise<void> => {
const asset = input as Asset
if (!asset.downloadAsync) {
throw new CodedError(
'ERR_FONT_SOURCE',
'`loadSingleFontAsync` expected resource of type `Asset` from expo-asset on native',
)
}
await asset.downloadAsync()
if (!asset.downloaded) {
throw new CodedError('ERR_DOWNLOAD', `Failed to download asset for font "${name}"`)
}
await ExpoFontLoader.loadAsync(FontLoader.getNativeFontName(name), asset.localUri)
}
}
// intentionally sorted manually
/* eslint-disable simple-import-sort/imports */
// https://reactnavigation.org/docs/getting-started/#installing-dependencies-into-a-bare-react-native-project
import 'react-native-gesture-handler'
import './index.expo-constants.hacks.ts'
// https://docs.expo.io/bare/installing-updates/#configuration-in-javascript-and-json
import 'expo-asset'
// originally from: node_modules/expo/AppEntry.js
import registerRootComponent from 'expo/build/launch/registerRootComponent'
import App from './App'
registerRootComponent(App)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment