Use autolinking react-native-config output in iOS artifacts generator (#53503)

Summary:
Resolves https://github.com/facebook/react-native/issues/53501

This is a pretty major oversight of (presumably) the old autolinking refactor. The iOS autolinking's second stage, invoked in `use_react_native!` does not accept the `react-native-config` sub-command's `react-native-config` output. This is only invoked and used in the prior step, `use_native_modules`.

The second step instead invokes old code that does something _similar_ to the new autolinking in `scripts/generate-artifacts-executor`, and happens to align in most cases. (But it does "autolinking" from scratch). tl;dr: When the results don't match up, things go wrong.

Instead, we now write the autolinking (react native config) results to a file, then read the output back in the second step.

This doesn't affect Android/Gradle, which are implemented correctly.

[IOS] [FIXED] - Use autolinking-generated react-native-config output in second step of cocoapods linking that generates artifacts and generated source

Pull Request resolved: https://github.com/facebook/react-native/pull/53503

Test Plan:
- See https://github.com/facebook/react-native/issues/53501 for failing repro
- Clone for working repro: https://github.com/byCedric/react-native-codegen-ios-autolinking/tree/fix-54503
  - Note: Contains this PR's changes as a patch
  - `bun install`
  - `bun expo run:ios`

Reviewed By: cortinico

Differential Revision: D81490755

Pulled By: cipolleschi

fbshipit-source-id: eefe786a116404f4ed24bd7125dfb108a811f71e
This commit is contained in:
Phil Pluckthun
2025-09-10 11:59:04 +00:00
committed by React Native Bot
parent 97b23a3462
commit bb73315a3f
4 changed files with 53 additions and 12 deletions
@@ -40,6 +40,12 @@ def list_native_modules!(config_command)
packages = config["dependencies"]
ios_project_root = Pathname.new(config["project"]["ios"]["sourceDir"])
react_native_path = Pathname.new(config["reactNativePath"])
codegen_output_path = ios_project_root.join("build/generated/autolinking/autolinking.json")
# Write autolinking react-native-config output to codegen folder
FileUtils.mkdir_p(File.dirname(codegen_output_path))
File.write(codegen_output_path, json)
found_pods = []
packages.each do |package_name, package|
@@ -87,7 +87,7 @@ class CodegenUtils
codegen_path = file_manager.join(ios_folder, codegen_dir)
return if !dir_manager.exist?(codegen_path)
FileUtils.rm_rf(dir_manager.glob("#{codegen_path}/*"))
FileUtils.rm_rf("#{codegen_path}")
base_provider_path = file_manager.join(rn_path, 'React', 'Fabric', 'RCTThirdPartyFabricComponentsProvider')
FileUtils.rm_rf("#{base_provider_path}.h")
FileUtils.rm_rf("#{base_provider_path}.mm")
@@ -86,10 +86,14 @@ function execute(
buildCodegenIfNeeded();
}
const reactNativeConfig = readReactNativeConfig(projectRoot);
const reactNativeConfig = readReactNativeConfig(
projectRoot,
baseOutputPath,
);
const codegenEnabledLibraries = findCodegenEnabledLibraries(
pkgJson,
projectRoot,
baseOutputPath,
reactNativeConfig,
);
@@ -97,15 +97,40 @@ function cleanupEmptyFilesAndFolders(filepath /*: string */) {
}
}
function readReactNativeConfig(projectRoot /*: string */) /*: $FlowFixMe */ {
const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js');
function readGeneratedAutolinkingOutput(
baseOutputPath /*: string */,
) /*: $FlowFixMe */ {
// NOTE: Generated by scripts/cocoapods/autolinking.rb in list_native_modules (called by use_native_modules)
const autolinkingGeneratedPath = path.resolve(
baseOutputPath,
'build/generated/autolinking/autolinking.json',
);
if (fs.existsSync(autolinkingGeneratedPath)) {
// $FlowFixMe[unsupported-syntax]
return require(autolinkingGeneratedPath);
} else {
codegenLog(
`Could not find generated autolinking output at: ${autolinkingGeneratedPath}`,
);
return null;
}
}
if (!fs.existsSync(rnConfigFilePath)) {
function readReactNativeConfig(
projectRoot /*: string */,
baseOutputPath /*: string */,
) /*: $FlowFixMe */ {
const autolinkingOutput = readGeneratedAutolinkingOutput(baseOutputPath);
const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js');
if (autolinkingOutput) {
return autolinkingOutput;
} else if (fs.existsSync(rnConfigFilePath)) {
// $FlowIgnore[unsupported-syntax]
return require(rnConfigFilePath);
} else {
codegenLog(`Could not find React Native config at: ${rnConfigFilePath}`);
return {};
}
// $FlowIgnore[unsupported-syntax]
return require(rnConfigFilePath);
}
/**
@@ -114,17 +139,23 @@ function readReactNativeConfig(projectRoot /*: string */) /*: $FlowFixMe */ {
function findCodegenEnabledLibraries(
pkgJson /*: $FlowFixMe */,
projectRoot /*: string */,
baseOutputPath /*: string */,
reactNativeConfig /*: $FlowFixMe */,
) /*: Array<$FlowFixMe> */ {
const projectLibraries = findProjectRootLibraries(pkgJson, projectRoot);
if (pkgJsonIncludesGeneratedCode(pkgJson)) {
return projectLibraries;
} else {
return [
...projectLibraries,
...findExternalLibraries(pkgJson, projectRoot),
const libraries = [...projectLibraries];
// If we ran autolinking, we shouldn't try to run our own "autolinking-like"
// library discovery
if (!readGeneratedAutolinkingOutput(baseOutputPath)) {
libraries.push(...findExternalLibraries(pkgJson, projectRoot));
}
libraries.push(
...findLibrariesFromReactNativeConfig(projectRoot, reactNativeConfig),
];
);
return libraries;
}
}