1/**
2 * These are the versioned first-party plugins with some of the future third-party plugins mixed in for legacy support.
3 */
4import {
5  AndroidConfig,
6  ConfigPlugin,
7  IOSConfig,
8  StaticPlugin,
9  withPlugins,
10  withStaticPlugin,
11} from '@expo/config-plugins';
12import { ExpoConfig } from '@expo/config-types';
13import Debug from 'debug';
14
15import { shouldSkipAutoPlugin } from '../getAutolinkedPackages';
16import { withAndroidIcons } from './icons/withAndroidIcons';
17import { withIosIcons } from './icons/withIosIcons';
18import withAdMob from './unversioned/expo-ads-admob/expo-ads-admob';
19import withAppleAuthentication from './unversioned/expo-apple-authentication';
20import withBranch from './unversioned/expo-branch/expo-branch';
21import withContacts from './unversioned/expo-contacts';
22import withDocumentPicker from './unversioned/expo-document-picker';
23import withFacebook from './unversioned/expo-facebook/expo-facebook';
24import withNavigationBar from './unversioned/expo-navigation-bar/expo-navigation-bar';
25import withNotifications from './unversioned/expo-notifications/expo-notifications';
26import withSplashScreen from './unversioned/expo-splash-screen/expo-splash-screen';
27import withSystemUI from './unversioned/expo-system-ui/expo-system-ui';
28import withUpdates from './unversioned/expo-updates';
29import withMaps from './unversioned/react-native-maps';
30
31const debug = Debug('expo:prebuild-config');
32
33/**
34 * Config plugin to apply all of the custom Expo iOS config plugins we support by default.
35 * TODO: In the future most of this should go into versioned packages like expo-facebook, expo-updates, etc...
36 */
37export const withIosExpoPlugins: ConfigPlugin<{
38  bundleIdentifier: string;
39}> = (config, { bundleIdentifier }) => {
40  // Set the bundle ID ahead of time.
41  if (!config.ios) config.ios = {};
42  config.ios.bundleIdentifier = bundleIdentifier;
43
44  return withPlugins(config, [
45    [IOSConfig.BundleIdentifier.withBundleIdentifier, { bundleIdentifier }],
46    IOSConfig.Swift.withSwiftBridgingHeader,
47    IOSConfig.Swift.withNoopSwiftFile,
48    IOSConfig.Google.withGoogle,
49    IOSConfig.Name.withDisplayName,
50    IOSConfig.Name.withProductName,
51    IOSConfig.Orientation.withOrientation,
52    IOSConfig.RequiresFullScreen.withRequiresFullScreen,
53    IOSConfig.Scheme.withScheme,
54    IOSConfig.UsesNonExemptEncryption.withUsesNonExemptEncryption,
55    IOSConfig.Version.withBuildNumber,
56    IOSConfig.Version.withVersion,
57    IOSConfig.Google.withGoogleServicesFile,
58    IOSConfig.BuildProperties.withJsEnginePodfileProps,
59    // Entitlements
60    IOSConfig.Entitlements.withAssociatedDomains,
61    // XcodeProject
62    IOSConfig.DeviceFamily.withDeviceFamily,
63    IOSConfig.Bitcode.withBitcode,
64    IOSConfig.Locales.withLocales,
65    // Dangerous
66    withIosIcons,
67  ]);
68};
69
70/**
71 * Config plugin to apply all of the custom Expo Android config plugins we support by default.
72 * TODO: In the future most of this should go into versioned packages like expo-facebook, expo-updates, etc...
73 */
74export const withAndroidExpoPlugins: ConfigPlugin<{
75  package: string;
76}> = (config, props) => {
77  // Set the package name ahead of time.
78  if (!config.android) config.android = {};
79  config.android.package = props.package;
80
81  return withPlugins(config, [
82    // gradle.properties
83    AndroidConfig.BuildProperties.withJsEngineGradleProps,
84
85    // settings.gradle
86    AndroidConfig.Name.withNameSettingsGradle,
87
88    // project build.gradle
89    AndroidConfig.GoogleServices.withClassPath,
90
91    // app/build.gradle
92    AndroidConfig.GoogleServices.withApplyPlugin,
93    AndroidConfig.Package.withPackageGradle,
94    AndroidConfig.Version.withVersion,
95
96    // AndroidManifest.xml
97    AndroidConfig.Package.withPackageManifest,
98    AndroidConfig.AllowBackup.withAllowBackup,
99    AndroidConfig.WindowSoftInputMode.withWindowSoftInputMode,
100    // Note: The withAndroidIntentFilters plugin must appear before the withScheme
101    // plugin or withScheme will override the output of withAndroidIntentFilters.
102    AndroidConfig.IntentFilters.withAndroidIntentFilters,
103    AndroidConfig.Scheme.withScheme,
104    AndroidConfig.Orientation.withOrientation,
105    AndroidConfig.Permissions.withInternalBlockedPermissions,
106    AndroidConfig.Permissions.withPermissions,
107
108    // strings.xml
109    AndroidConfig.Name.withName,
110
111    // Dangerous -- these plugins run in reverse order.
112    AndroidConfig.GoogleServices.withGoogleServicesFile,
113
114    // Modify colors.xml and styles.xml
115    AndroidConfig.StatusBar.withStatusBar,
116    AndroidConfig.PrimaryColor.withPrimaryColor,
117
118    withAndroidIcons,
119    // If we renamed the package, we should also move it around and rename it in source files
120    // Added last to ensure this plugin runs first. Out of tree solutions will mistakenly resolve the package incorrectly otherwise.
121    AndroidConfig.Package.withPackageRefactor,
122  ]);
123};
124
125// Must keep in sync with `withVersionedExpoSDKPlugins`
126const versionedExpoSDKPackages: string[] = [
127  'react-native-maps',
128  'expo-ads-admob',
129  'expo-apple-authentication',
130  'expo-contacts',
131  'expo-notifications',
132  'expo-updates',
133  'expo-branch',
134  'expo-navigation-bar',
135  'expo-document-picker',
136  'expo-facebook',
137  'expo-splash-screen',
138  'expo-system-ui',
139];
140
141export const withVersionedExpoSDKPlugins: ConfigPlugin<{ expoUsername: string | null }> = (
142  config,
143  { expoUsername }
144) => {
145  return withPlugins(config, [
146    withMaps,
147    withAdMob,
148    withAppleAuthentication,
149    withContacts,
150    withNotifications,
151    [withUpdates, { expoUsername }],
152    withBranch,
153    withDocumentPicker,
154    withFacebook,
155    // System UI must come before splash screen as they overlap
156    // and splash screen will warn about conflicting rules.
157    withSystemUI,
158    withSplashScreen,
159    withNavigationBar,
160  ]);
161};
162
163export function getAutoPlugins() {
164  return versionedExpoSDKPackages.concat(legacyExpoPlugins).concat(expoManagedVersionedPlugins);
165}
166
167export function getLegacyExpoPlugins() {
168  return legacyExpoPlugins;
169}
170
171// Expo managed packages that require extra update.
172// These get applied automatically to create parity with expo build in eas build.
173const legacyExpoPlugins = [
174  'expo-app-auth',
175  'expo-av',
176  'expo-background-fetch',
177  'expo-barcode-scanner',
178  'expo-brightness',
179  'expo-calendar',
180  'expo-camera',
181  'expo-cellular',
182  'expo-dev-menu',
183  'expo-dev-launcher',
184  'expo-dev-client',
185  'expo-image-picker',
186  'expo-file-system',
187  'expo-ads-facebook',
188  'expo-location',
189  'expo-media-library',
190  'expo-screen-orientation',
191  'expo-sensors',
192  'expo-task-manager',
193  'expo-local-authentication',
194];
195
196// Plugins that need to be automatically applied, but also get applied by expo-cli if the versioned plugin isn't available.
197// These are split up because the user doesn't need to be prompted to setup these packages.
198const expoManagedVersionedPlugins = [
199  'expo-firebase-analytics',
200  'expo-firebase-core',
201  'expo-google-sign-in',
202];
203
204const withOptionalLegacyPlugins: ConfigPlugin<(StaticPlugin | string)[]> = (config, plugins) => {
205  return plugins.reduce((prev, plugin) => {
206    if (shouldSkipAutoPlugin(config, plugin)) {
207      debug('Skipping unlinked auto plugin:', plugin);
208      return prev;
209    }
210
211    return withStaticPlugin(prev, {
212      // hide errors
213      _isLegacyPlugin: true,
214      plugin,
215      // If a plugin doesn't exist, do nothing.
216      fallback: (config) => config,
217    });
218  }, config);
219};
220
221export function withLegacyExpoPlugins(config: ExpoConfig) {
222  return withOptionalLegacyPlugins(config, [
223    ...new Set(expoManagedVersionedPlugins.concat(legacyExpoPlugins)),
224  ]);
225}
226