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