diff --git a/packages/community-cli-plugin/package.json b/packages/community-cli-plugin/package.json index c065551b548..46cd85ded78 100644 --- a/packages/community-cli-plugin/package.json +++ b/packages/community-cli-plugin/package.json @@ -35,7 +35,8 @@ "metro-resolver": "^0.82.4" }, "peerDependencies": { - "@react-native-community/cli": "*" + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" }, "peerDependenciesMeta": { "@react-native-community/cli": { diff --git a/packages/community-cli-plugin/src/utils/loadMetroConfig.js b/packages/community-cli-plugin/src/utils/loadMetroConfig.js index 39ada374478..36d11175f63 100644 --- a/packages/community-cli-plugin/src/utils/loadMetroConfig.js +++ b/packages/community-cli-plugin/src/utils/loadMetroConfig.js @@ -13,7 +13,7 @@ import type {ConfigT, InputConfigT, YargArguments} from 'metro-config'; import {CLIError} from './errors'; import {reactNativePlatformResolver} from './metroPlatformResolver'; -import {loadConfig, mergeConfig, resolveConfig} from 'metro-config'; +import {loadConfig, resolveConfig} from 'metro-config'; import path from 'path'; const debug = require('debug')('ReactNative:CommunityCliPlugin'); @@ -30,7 +30,7 @@ export type ConfigLoadingContext = $ReadOnly<{ /** * Get the config options to override based on RN CLI inputs. */ -function getOverrideConfig( +function getCommunityCliDefaultConfig( ctx: ConfigLoadingContext, config: ConfigT, ): InputConfigT { @@ -85,6 +85,31 @@ export default async function loadMetroConfig( ctx: ConfigLoadingContext, options: YargArguments = {}, ): Promise { + let RNMetroConfig = null; + try { + RNMetroConfig = require('@react-native/metro-config'); + } catch (e) { + throw new Error( + "Cannot resolve `@react-native/metro-config`. Ensure it is listed in your project's `devDependencies`.", + ); + } + + // Get the RN defaults before our customisations + const defaultConfig = RNMetroConfig.getDefaultConfig(ctx.root); + // Unflag the config as being loaded - it must be loaded again in userland. + global.__REACT_NATIVE_METRO_CONFIG_LOADED = false; + + // Add our defaults to `@react-native/metro-config` before the user config + // loads them. + if (typeof RNMetroConfig.setFrameworkDefaults !== 'function') { + throw new Error( + '`@react-native/metro-config` does not have the expected API. Ensure it matches your React Native version.', + ); + } + RNMetroConfig.setFrameworkDefaults( + getCommunityCliDefaultConfig(ctx, defaultConfig), + ); + const cwd = ctx.root; const projectConfig = await resolveConfig(options.config, cwd); @@ -108,13 +133,8 @@ This warning will be removed in future (https://github.com/facebook/metro/issues console.warn(line); } } - - const config = await loadConfig({ + return loadConfig({ cwd, ...options, }); - - const overrideConfig = getOverrideConfig(ctx, config); - - return mergeConfig(config, overrideConfig); } diff --git a/packages/metro-config/src/index.flow.js b/packages/metro-config/src/index.flow.js index e20df145cf2..910a9bbe52c 100644 --- a/packages/metro-config/src/index.flow.js +++ b/packages/metro-config/src/index.flow.js @@ -8,7 +8,7 @@ * @format */ -import type {ConfigT} from 'metro-config'; +import type {ConfigT, InputConfigT} from 'metro-config'; import {getDefaultConfig as getBaseConfig, mergeConfig} from 'metro-config'; @@ -43,11 +43,16 @@ const INTERNAL_CALLSITES_REGEX = new RegExp( export {mergeConfig} from 'metro-config'; +let frameworkDefaults = {}; +export function setFrameworkDefaults(config: InputConfigT) { + frameworkDefaults = config; +} + /** * Get the base Metro configuration for a React Native project. */ export function getDefaultConfig(projectRoot: string): ConfigT { - const config = { + const reactNativeDefaults = { resolver: { resolverMainFields: ['react-native', 'browser', 'main'], platforms: ['android', 'ios'], @@ -99,5 +104,7 @@ export function getDefaultConfig(projectRoot: string): ConfigT { // Set global hook so that the CLI can detect when this config has been loaded global.__REACT_NATIVE_METRO_CONFIG_LOADED = true; - return mergeConfig(getBaseConfig.getDefaultValues(projectRoot), config); + const metroDefaults = getBaseConfig.getDefaultValues(projectRoot); + + return mergeConfig(metroDefaults, reactNativeDefaults, frameworkDefaults); }