Skip to content

Commit 02aa02e

Browse files
chrfalchclaude
andcommitted
fix(spm): detect install_modules_dependencies as an implicit React-core dep
rn-tester's test libraries (TestLibraryApple / TestLibraryCommon) failed to build under SwiftPM with `RCTDeprecation/RCTDeprecation.h file not found`: the scaffolded Package.swift carried no React products because the scaffolder's `coreReactNative` detection missed them. Both are plain ObjC modules that wire React core ONLY through the `install_modules_dependencies(s)` podspec helper and ship no `codegenConfig`. The scaffolder's two detection paths both fall through for that shape: 1. Explicit `s.dependency "React-Core"` — but the helper is stripped before `pod ipc spec` (and its `min_supported_versions` call makes pod-ipc fail outright, falling back to the regex parser, which can't expand the helper either), so React-Core never surfaces in the parsed dependencies. 2. package.json `codegenConfig` — only marks Fabric / New-Arch libraries, not plain ObjC modules. Record whether the podspec source calls `install_modules_dependencies` (visible even though the helper is stripped for pod-ipc) and treat its presence as an authoritative React-core signal alongside codegenConfig — injecting that family is the helper's entire job. - read-podspec.js: set `usesInstallModulesDependencies` from the podspec source - scaffold-package-swift.js: OR it into the coreReactNative fallback - spm-types.js: add the field to PodspecModel Verified: TestLibraryApple/Common now scaffold with the full React product set (ReactNative, ReactNativeHeaders, ReactNativeDependenciesHeaders, ReactAppHeaders); rn-tester BUILD SUCCEEDED. New focused unit test covers the plain-ObjC-module-with-install_modules_dependencies shape. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent fdea8af commit 02aa02e

4 files changed

Lines changed: 59 additions & 2 deletions

File tree

packages/react-native/scripts/spm/__tests__/scaffold-package-swift-test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function podspec(overrides /*: Object */ = {}) {
4646
requiresArc: true,
4747
warnings: [],
4848
partial: false,
49+
usesInstallModulesDependencies: false,
4950
...overrides,
5051
};
5152
}
@@ -341,6 +342,31 @@ describe('translatePodspecToSpmTarget', () => {
341342
}
342343
});
343344

345+
it('treats install_modules_dependencies (no codegenConfig) as an implicit React-core dep (rn-tester TestLibrary shape)', () => {
346+
// A plain ObjC module (rn-tester's TestLibraryApple/Common) wires React
347+
// core ONLY via install_modules_dependencies(s) and has NO codegenConfig.
348+
// The stripped helper leaves model.dependencies without React-Core, so the
349+
// usesInstallModulesDependencies marker is what keeps coreReactNative true.
350+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'imd-dep-'));
351+
try {
352+
fs.writeFileSync(
353+
path.join(root, 'package.json'),
354+
JSON.stringify({name: 'TestLibraryApple'}), // no codegenConfig
355+
);
356+
const model = podspec({
357+
dependencies: [],
358+
usesInstallModulesDependencies: true,
359+
});
360+
const spec = translatePodspecToSpmTarget(
361+
model,
362+
autolinkedDep({name: 'TestLibraryApple', root}),
363+
);
364+
expect(spec.coreReactNative).toBe(true);
365+
} finally {
366+
fs.rmSync(root, {recursive: true, force: true});
367+
}
368+
});
369+
344370
it('does NOT force coreReactNative for a non-codegen dep with no React deps', () => {
345371
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'no-codegen-dep-'));
346372
try {

packages/react-native/scripts/spm/read-podspec.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ function flattenSubspecs(rawSpec /*: RawSpec */) /*: PodspecModel */ {
631631
requiresArc,
632632
warnings,
633633
partial,
634+
// Default: overridden in readPodspec, which can see the podspec source.
635+
usesInstallModulesDependencies: false,
634636
};
635637
}
636638

@@ -654,7 +656,20 @@ function readPodspec(podspecPath /*: string */) /*: PodspecModel */ {
654656
}
655657
const podIpc = runPodIpcSpec(podspecPath);
656658
const raw = podIpc != null ? podIpc : regexPodspec(podspecPath);
657-
return flattenSubspecs(raw);
659+
const model = flattenSubspecs(raw);
660+
// Record whether the podspec calls `install_modules_dependencies(s)`. We
661+
// strip that helper before `pod ipc` and the regex parser can't expand it,
662+
// so the React-Core family it injects never lands in `model.dependencies`.
663+
// The scaffolder uses this as an implicit React-core marker (plain ObjC
664+
// modules using the helper have no `codegenConfig` to detect instead).
665+
try {
666+
const src = fs.readFileSync(podspecPath, 'utf8');
667+
model.usesInstallModulesDependencies =
668+
/\binstall_modules_dependencies\b/.test(src);
669+
} catch {
670+
// best-effort; leave the flattenSubspecs default (false)
671+
}
672+
return model;
658673
}
659674

660675
module.exports = {

packages/react-native/scripts/spm/scaffold-package-swift.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,16 @@ function translatePodspecToSpmTarget(
360360
// headers live in the per-app React-GeneratedCode package, so the lib must
361361
// depend on it (and, transitively, on React core). Treat codegenConfig as
362362
// an implicit React-core dependency.
363-
if (!coreReactNative && depHasCodegenConfig(dep.root)) {
363+
// `codegenConfig` only marks Fabric/New-Arch libraries. A plain ObjC module
364+
// (e.g. rn-tester's TestLibrary*) that wires React core solely through
365+
// `install_modules_dependencies(s)` has no codegenConfig — but the helper's
366+
// presence is itself an authoritative "depends on React core" signal, so
367+
// treat it the same way.
368+
if (
369+
!coreReactNative &&
370+
(depHasCodegenConfig(dep.root) ||
371+
model.usesInstallModulesDependencies === true)
372+
) {
364373
coreReactNative = true;
365374
}
366375

packages/react-native/scripts/spm/spm-types.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,13 @@ export type PodspecModel = {
299299
// emit a louder banner in the scaffold summary explaining that dependency
300300
// wiring may be incomplete.
301301
partial: boolean,
302+
// True when the podspec source calls `install_modules_dependencies(s)` —
303+
// RN's Podfile-side helper that injects the React-Core family. We strip that
304+
// helper before evaluating the spec (and the regex parser can't expand it),
305+
// so React-Core never surfaces in `dependencies`. The scaffolder treats this
306+
// flag as an implicit React-core dependency (a plain ObjC module using the
307+
// helper has no `codegenConfig` to key off).
308+
usesInstallModulesDependencies: boolean,
302309
};
303310
304311
// Intermediate translation result — concrete data the Swift emitter consumes.

0 commit comments

Comments
 (0)