109bb6093SEvan Baconimport { ExpoConfig } from '@expo/config'; 209bb6093SEvan Baconimport { 309bb6093SEvan Bacon normalizeStaticPlugin, 409bb6093SEvan Bacon resolveConfigPluginFunctionWithInfo, 509bb6093SEvan Bacon} from '@expo/config-plugins/build/utils/plugin-resolver'; 609bb6093SEvan Baconimport { getAutoPlugins } from '@expo/prebuild-config'; 709bb6093SEvan Bacon 809bb6093SEvan Baconimport { attemptAddingPluginsAsync } from '../../utils/modifyConfigPlugins'; 909bb6093SEvan Bacon 10*474a7a4bSEvan Baconconst debug = require('debug')('expo:install:config-plugins') as typeof console.log; 11*474a7a4bSEvan Bacon 1209bb6093SEvan Baconconst AUTO_PLUGINS = getAutoPlugins(); 1309bb6093SEvan Bacon 1409bb6093SEvan Bacon/** 1509bb6093SEvan Bacon * Resolve if a package has a config plugin. 1609bb6093SEvan Bacon * For sanity, we'll only support config plugins that use the `app.config.js` entry file, 1709bb6093SEvan Bacon * this is because a package like `lodash` could be a "valid" config plugin and break the prebuild process. 1809bb6093SEvan Bacon * 1909bb6093SEvan Bacon * @param projectRoot 2009bb6093SEvan Bacon * @param packageName 2109bb6093SEvan Bacon * @returns 2209bb6093SEvan Bacon */ 2309bb6093SEvan Baconfunction packageHasConfigPlugin(projectRoot: string, packageName: string) { 2409bb6093SEvan Bacon try { 2509bb6093SEvan Bacon const info = resolveConfigPluginFunctionWithInfo(projectRoot, packageName); 2609bb6093SEvan Bacon if (info.isPluginFile) { 2709bb6093SEvan Bacon return info.plugin; 2809bb6093SEvan Bacon } 2909bb6093SEvan Bacon } catch {} 3009bb6093SEvan Bacon return false; 3109bb6093SEvan Bacon} 3209bb6093SEvan Bacon 3309bb6093SEvan Bacon/** 3409bb6093SEvan Bacon * Get a list of plugins that were are supplied as string module IDs. 3509bb6093SEvan Bacon * @example 3609bb6093SEvan Bacon * ```json 3709bb6093SEvan Bacon * { 3809bb6093SEvan Bacon * "plugins": [ 3909bb6093SEvan Bacon * "expo-camera", 4009bb6093SEvan Bacon * ["react-native-firebase", ...] 4109bb6093SEvan Bacon * ] 4209bb6093SEvan Bacon * } 4309bb6093SEvan Bacon * ``` 4409bb6093SEvan Bacon * ↓ ↓ ↓ ↓ ↓ ↓ 4509bb6093SEvan Bacon * 4609bb6093SEvan Bacon * `['expo-camera', 'react-native-firebase']` 4709bb6093SEvan Bacon * 4809bb6093SEvan Bacon */ 4909bb6093SEvan Baconexport function getNamedPlugins(plugins: NonNullable<ExpoConfig['plugins']>): string[] { 5009bb6093SEvan Bacon const namedPlugins: string[] = []; 5109bb6093SEvan Bacon for (const plugin of plugins) { 5209bb6093SEvan Bacon try { 5309bb6093SEvan Bacon // @ts-ignore 5409bb6093SEvan Bacon const [normal] = normalizeStaticPlugin(plugin); 5509bb6093SEvan Bacon if (typeof normal === 'string') { 5609bb6093SEvan Bacon namedPlugins.push(normal); 5709bb6093SEvan Bacon } 5809bb6093SEvan Bacon } catch { 5909bb6093SEvan Bacon // ignore assertions 6009bb6093SEvan Bacon } 6109bb6093SEvan Bacon } 6209bb6093SEvan Bacon return namedPlugins; 6309bb6093SEvan Bacon} 6409bb6093SEvan Bacon 6509bb6093SEvan Bacon/** Attempts to ensure that non-auto plugins are added to the `app.json` `plugins` array when modules with Expo Config Plugins are installed. */ 6609bb6093SEvan Baconexport async function autoAddConfigPluginsAsync( 6709bb6093SEvan Bacon projectRoot: string, 6809bb6093SEvan Bacon exp: Pick<ExpoConfig, 'plugins'>, 6909bb6093SEvan Bacon packages: string[] 7009bb6093SEvan Bacon) { 71*474a7a4bSEvan Bacon debug('Checking config plugins...'); 7209bb6093SEvan Bacon 7309bb6093SEvan Bacon const currentPlugins = exp.plugins || []; 7409bb6093SEvan Bacon const normalized = getNamedPlugins(currentPlugins); 7509bb6093SEvan Bacon 76*474a7a4bSEvan Bacon debug(`Existing plugins: ${normalized.join(', ')}`); 7709bb6093SEvan Bacon 7809bb6093SEvan Bacon const plugins = packages.filter((pkg) => { 7909bb6093SEvan Bacon if (normalized.includes(pkg)) { 8009bb6093SEvan Bacon // already included in plugins array 8109bb6093SEvan Bacon return false; 8209bb6093SEvan Bacon } 8309bb6093SEvan Bacon // Check if the package has a valid plugin. Must be a well-made plugin for it to work with this. 8409bb6093SEvan Bacon const plugin = packageHasConfigPlugin(projectRoot, pkg); 8509bb6093SEvan Bacon 86*474a7a4bSEvan Bacon debug(`Package "${pkg}" has plugin: ${!!plugin}` + (plugin ? ` (args: ${plugin.length})` : '')); 8709bb6093SEvan Bacon 8809bb6093SEvan Bacon if (AUTO_PLUGINS.includes(pkg)) { 89*474a7a4bSEvan Bacon debug(`Package "${pkg}" is an auto plugin, skipping...`); 9009bb6093SEvan Bacon return false; 9109bb6093SEvan Bacon } 9209bb6093SEvan Bacon 9309bb6093SEvan Bacon return !!plugin; 9409bb6093SEvan Bacon }); 9509bb6093SEvan Bacon 9609bb6093SEvan Bacon await attemptAddingPluginsAsync(projectRoot, exp, plugins); 9709bb6093SEvan Bacon} 98