Skip to content

Commit 847da2a

Browse files
CAMOBAPclaude
andcommitted
fix(codegen): read react-native.config.cjs so ESM projects are not silently skipped
readReactNativeConfig resolves the config with a single hardcoded extension: const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js'); A project whose package.json declares "type": "module" cannot use that filename. Node parses .js as ESM there, and the require() two lines below either throws (CommonJS body -- uncaught here, so codegen dies) or returns {__esModule, default}, so rnConfig.dependencies is undefined and findLibrariesFromReactNativeConfig returns [] without logging. .cjs is the only extension that is both require()-able and legal in an ESM package, and @react-native-community/cli already treats it as first-class via searchPlacesForCJS. Codegen has its own resolver, does not use cosmiconfig, and was never brought in line. The result is that autolinking and codegen disagree about the same file: autolinking reads the config and installs the pod, codegen does not and generates nothing for that library, and the pod then fails to compile because its Fabric component includes a header that was never generated. Probe .cjs in addition to .js. .js is checked first, so projects that have one resolve exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 9b2a54e commit 847da2a

2 files changed

Lines changed: 58 additions & 3 deletions

File tree

packages/react-native/scripts/codegen/__tests__/generate-artifacts-executor-test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const {
2121
const {
2222
cleanupEmptyFilesAndFolders,
2323
extractLibrariesFromJSON,
24+
readReactNativeConfig,
2425
} = require('../generate-artifacts-executor/utils');
2526
const fs = require('node:fs');
2627
const os = require('node:os');
@@ -142,6 +143,58 @@ const packageJson = JSON.stringify({
142143
});
143144
});
144145

146+
describe('readReactNativeConfig', () => {
147+
const CONFIG_JS = "module.exports = {dependencies: {'from-js': {root: '/js'}}};";
148+
const CONFIG_CJS = "module.exports = {dependencies: {'from-cjs': {root: '/cjs'}}};";
149+
150+
function withProjectRoot(files, assertion) {
151+
const projectRoot = fs.mkdtempSync(
152+
path.join(os.tmpdir(), 'react-native-codegen-config-'),
153+
);
154+
try {
155+
for (const [name, contents] of Object.entries(files)) {
156+
fs.writeFileSync(path.join(projectRoot, name), contents);
157+
}
158+
// baseOutputPath is a directory with no generated autolinking output, so
159+
// resolution falls through to the react-native.config file.
160+
assertion(readReactNativeConfig(projectRoot, projectRoot));
161+
} finally {
162+
fs.rmSync(projectRoot, {recursive: true, force: true});
163+
}
164+
}
165+
166+
it('reads react-native.config.js', () => {
167+
withProjectRoot({'react-native.config.js': CONFIG_JS}, config => {
168+
expect(config.dependencies).toHaveProperty('from-js');
169+
});
170+
});
171+
172+
it('reads react-native.config.cjs when there is no .js config', () => {
173+
withProjectRoot({'react-native.config.cjs': CONFIG_CJS}, config => {
174+
expect(config.dependencies).toHaveProperty('from-cjs');
175+
});
176+
});
177+
178+
it('prefers react-native.config.js when both exist', () => {
179+
withProjectRoot(
180+
{
181+
'react-native.config.js': CONFIG_JS,
182+
'react-native.config.cjs': CONFIG_CJS,
183+
},
184+
config => {
185+
expect(config.dependencies).toHaveProperty('from-js');
186+
expect(config.dependencies).not.toHaveProperty('from-cjs');
187+
},
188+
);
189+
});
190+
191+
it('returns an empty config when neither exists', () => {
192+
withProjectRoot({}, config => {
193+
expect(config).toEqual({});
194+
});
195+
});
196+
});
197+
145198
describe('extractSupportedApplePlatforms', () => {
146199
it('extracts platforms when podspec specifies object of platforms', () => {
147200
const myDependency = 'test-library';

packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,16 @@ function readReactNativeConfig(
126126
projectRoot,
127127
baseOutputPath,
128128
);
129-
const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js');
129+
const rnConfigFilePath = ['react-native.config.js', 'react-native.config.cjs']
130+
.map(fileName => path.resolve(projectRoot, fileName))
131+
.find(candidatePath => fs.existsSync(candidatePath));
130132
if (autolinkingOutput) {
131133
return autolinkingOutput;
132-
} else if (fs.existsSync(rnConfigFilePath)) {
134+
} else if (rnConfigFilePath != null) {
133135
// $FlowFixMe[unsupported-syntax]
134136
return require(rnConfigFilePath);
135137
} else {
136-
codegenLog(`Could not find React Native config at: ${rnConfigFilePath}`);
138+
codegenLog(`Could not find React Native config in: ${projectRoot}`);
137139
return {};
138140
}
139141
}

0 commit comments

Comments
 (0)