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 withNavigationBar from './unversioned/expo-navigation-bar/expo-navigation-bar';
24import withNotifications from './unversioned/expo-notifications/expo-notifications';
25import withSplashScreen from './unversioned/expo-splash-screen/expo-splash-screen';
26import withSystemUI from './unversioned/expo-system-ui/expo-system-ui';
27import withUpdates from './unversioned/expo-updates';
28import withMaps from './unversioned/react-native-maps';
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<{ expoUsername: string | null }> = (
139  config,
140  { expoUsername }
141) => {
142  return withPlugins(config, [
143    withMaps,
144    withAdMob,
145    withAppleAuthentication,
146    withContacts,
147    withNotifications,
148    [withUpdates, { expoUsername }],
149    withBranch,
150    withDocumentPicker,
151    // System UI must come before splash screen as they overlap
152    // and splash screen will warn about conflicting rules.
153    withSystemUI,
154    withSplashScreen,
155    withNavigationBar,
156  ]);
157};
158
159export function getAutoPlugins() {
160  return versionedExpoSDKPackages.concat(legacyExpoPlugins).concat(expoManagedVersionedPlugins);
161}
162
163export function getLegacyExpoPlugins() {
164  return legacyExpoPlugins;
165}
166
167// Expo managed packages that require extra update.
168// These get applied automatically to create parity with expo build in eas build.
169const legacyExpoPlugins = [
170  'expo-app-auth',
171  'expo-av',
172  'expo-background-fetch',
173  'expo-barcode-scanner',
174  'expo-brightness',
175  'expo-calendar',
176  'expo-camera',
177  'expo-cellular',
178  'expo-dev-menu',
179  'expo-dev-launcher',
180  'expo-dev-client',
181  'expo-image-picker',
182  'expo-file-system',
183  'expo-location',
184  'expo-media-library',
185  'expo-screen-orientation',
186  'expo-sensors',
187  'expo-task-manager',
188  'expo-local-authentication',
189];
190
191// Plugins that need to be automatically applied, but also get applied by expo-cli if the versioned plugin isn't available.
192// These are split up because the user doesn't need to be prompted to setup these packages.
193const expoManagedVersionedPlugins = [
194  'expo-firebase-analytics',
195  'expo-firebase-core',
196  'expo-google-sign-in',
197];
198
199const withOptionalLegacyPlugins: ConfigPlugin<(StaticPlugin | string)[]> = (config, plugins) => {
200  return plugins.reduce((prev, plugin) => {
201    if (shouldSkipAutoPlugin(config, plugin)) {
202      debug('Skipping unlinked auto plugin:', plugin);
203      return prev;
204    }
205
206    return withStaticPlugin(prev, {
207      // hide errors
208      _isLegacyPlugin: true,
209      plugin,
210      // If a plugin doesn't exist, do nothing.
211      fallback: (config) => config,
212    });
213  }, config);
214};
215
216export function withLegacyExpoPlugins(config: ExpoConfig) {
217  return withOptionalLegacyPlugins(config, [
218    ...new Set(expoManagedVersionedPlugins.concat(legacyExpoPlugins)),
219  ]);
220}
221