1const { getConfig, getNameFromConfig } = require('expo/config'); 2// Use root to work better with create-react-app 3const DEFAULT_LANGUAGE_ISO_CODE = `en`; 4const DEFAULT_DISPLAY = 'standalone'; 5const DEFAULT_STATUS_BAR = 'black-translucent'; 6const DEFAULT_PREFER_RELATED_APPLICATIONS = true; 7// Convert expo value to PWA value 8function ensurePWAorientation(orientation) { 9 if (orientation && typeof orientation === 'string') { 10 const webOrientation = orientation.toLowerCase(); 11 if (webOrientation !== 'default') { 12 return webOrientation; 13 } 14 } 15 return undefined; 16} 17 18const RESTRICTED_MANIFEST_FIELDS = [ 19 // Omit app.json properties that get removed during the native build 20 'facebookScheme', 21 'facebookAppId', 22 'facebookDisplayName', 23 // Remove iOS and Android. 24 'ios', 25 'android', 26 // Hide internal / build values 27 'plugins', 28 'hooks', 29 '_internal', 30 // Remove metro-specific values 31 'assetBundlePatterns', 32]; 33function getExpoConstantsManifest(projectRoot) { 34 const { exp } = getConfig(projectRoot, { 35 isPublicConfig: true, 36 skipSDKVersionRequirement: true, 37 }); 38 const manifest = ensurePWAConfig(exp); 39 for (const field of RESTRICTED_MANIFEST_FIELDS) { 40 delete manifest[field]; 41 } 42 return manifest; 43} 44function applyWebDefaults(appJSON) { 45 // For RN CLI support 46 // @ts-ignore: expo object doesn't exist on ExpoConfig 47 const appManifest = appJSON.expo || appJSON || {}; 48 const { web: webManifest = {}, splash = {}, ios = {}, android = {} } = appManifest; 49 // rn-cli apps use a displayName value as well. 50 const { appName, webName } = getNameFromConfig(appJSON); 51 const languageISOCode = webManifest.lang || DEFAULT_LANGUAGE_ISO_CODE; 52 const primaryColor = appManifest.primaryColor; 53 const description = appManifest.description; 54 // The theme_color sets the color of the tool bar, and may be reflected in the app's preview in task switchers. 55 const webThemeColor = webManifest.themeColor || primaryColor; 56 const dir = webManifest.dir; 57 const shortName = webManifest.shortName || webName; 58 const display = webManifest.display || DEFAULT_DISPLAY; 59 const startUrl = webManifest.startUrl; 60 const { scope, crossorigin } = webManifest; 61 const barStyle = webManifest.barStyle || DEFAULT_STATUS_BAR; 62 const orientation = ensurePWAorientation(webManifest.orientation || appManifest.orientation); 63 /** 64 * **Splash screen background color** 65 * `https://developers.google.com/web/fundamentals/web-app-manifest/#splash-screen` 66 * The background_color should be the same color as the load page, 67 * to provide a smooth transition from the splash screen to your app. 68 */ 69 const backgroundColor = webManifest.backgroundColor || splash.backgroundColor; // No default background color 70 /** 71 * 72 * https://developer.mozilla.org/en-US/docs/Web/Manifest#prefer_related_applications 73 * Specifies a boolean value that hints for the user agent to indicate 74 * to the user that the specified native applications (see below) are recommended over the website. 75 * This should only be used if the related native apps really do offer something that the website can't... like Expo ;) 76 * 77 * >> The banner won't show up if the app is already installed: 78 * https://github.com/GoogleChrome/samples/issues/384#issuecomment-326387680 79 */ 80 const preferRelatedApplications = 81 webManifest.preferRelatedApplications === undefined 82 ? DEFAULT_PREFER_RELATED_APPLICATIONS 83 : webManifest.preferRelatedApplications; 84 const relatedApplications = inferWebRelatedApplicationsFromConfig(appManifest); 85 return { 86 ...appManifest, 87 name: appName, 88 description, 89 primaryColor, 90 // Ensure these objects exist 91 ios: { 92 ...ios, 93 }, 94 android: { 95 ...android, 96 }, 97 web: { 98 ...webManifest, 99 meta: undefined, 100 build: undefined, 101 scope, 102 crossorigin, 103 description, 104 preferRelatedApplications, 105 relatedApplications, 106 startUrl, 107 shortName, 108 display, 109 orientation, 110 dir, 111 barStyle, 112 backgroundColor, 113 themeColor: webThemeColor, 114 lang: languageISOCode, 115 name: webName, 116 }, 117 }; 118} 119/** 120 * https://developer.mozilla.org/en-US/docs/Web/Manifest#related_applications 121 * An array of native applications that are installable by, or accessible to, the underlying platform 122 * for example, a native Android application obtainable through the Google Play Store. 123 * Such applications are intended to be alternatives to the 124 * website that provides similar/equivalent functionality — like the native app version of the website. 125 */ 126function inferWebRelatedApplicationsFromConfig({ web = {}, ios = {}, android = {} }) { 127 const relatedApplications = web.relatedApplications || []; 128 const { bundleIdentifier, appStoreUrl } = ios; 129 if (bundleIdentifier) { 130 const PLATFORM_ITUNES = 'itunes'; 131 const iosApp = relatedApplications.some(({ platform }) => platform === PLATFORM_ITUNES); 132 if (!iosApp) { 133 relatedApplications.push({ 134 platform: PLATFORM_ITUNES, 135 url: appStoreUrl, 136 id: bundleIdentifier, 137 }); 138 } 139 } 140 const { package: androidPackage, playStoreUrl } = android; 141 if (androidPackage) { 142 const PLATFORM_PLAY = 'play'; 143 const alreadyHasAndroidApp = relatedApplications.some( 144 ({ platform }) => platform === PLATFORM_PLAY 145 ); 146 if (!alreadyHasAndroidApp) { 147 relatedApplications.push({ 148 platform: PLATFORM_PLAY, 149 url: playStoreUrl || `http://play.google.com/store/apps/details?id=${androidPackage}`, 150 id: androidPackage, 151 }); 152 } 153 } 154 return relatedApplications; 155} 156function ensurePWAConfig(appJSON) { 157 const config = applyWebDefaults(appJSON); 158 return config; 159} 160 161module.exports = { 162 getExpoConstantsManifest, 163}; 164