Skip to content

Commit cd0fec1

Browse files
chrfalchclaude
andcommitted
feat(spm): drop consumer-side headers compose; don't ship build-only scripts
generate-spm-package previously composed the headers-spec layout on the consumer side when the prebuilt artifacts lacked ReactNativeHeaders.xcframework. That pulled the ios-prebuild build scripts (headers-compose and its deps) into the published npm package, which in turn required the `plist` dependency at consumer runtime. The prebuilt core artifact ships React.xcframework AND ReactNativeHeaders.xcframework together, so a consumer never needs to compose the layout. Remove the consumer-side compose: fail with a clear error if ReactNativeHeaders is absent, and stop listing scripts/ios-prebuild/* in the package files allowlist — those are full-repo prebuild scripts, not consumer runtime code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 69a07bc commit cd0fec1

3 files changed

Lines changed: 28 additions & 68 deletions

File tree

packages/react-native/package.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,6 @@
107107
"scripts/hermes/hermes-utils.js",
108108
"scripts/hermes/prepare-hermes-for-build.js",
109109
"scripts/ios-configure-glog.sh",
110-
"scripts/ios-prebuild/headers-compose.js",
111-
"scripts/ios-prebuild/headers-config.js",
112-
"scripts/ios-prebuild/headers-inventory.js",
113-
"scripts/ios-prebuild/headers-spec.js",
114-
"scripts/ios-prebuild/headers.js",
115-
"scripts/ios-prebuild/utils.js",
116110
"scripts/native_modules.rb",
117111
"scripts/node-binary.sh",
118112
"scripts/packager-reporter.js",

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

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ describe('main', () => {
157157
return dir;
158158
}
159159

160-
function run(artifactsDir /*:: ?: ?string */, deps /*:: ?: Object */) {
160+
function run(artifactsDir /*:: ?: ?string */) {
161161
const argv = [
162162
'--app-root',
163163
appRoot,
@@ -169,23 +169,20 @@ describe('main', () => {
169169
if (artifactsDir != null) {
170170
argv.push('--artifacts-dir', artifactsDir);
171171
}
172-
main(argv, deps);
172+
main(argv);
173173
}
174174

175175
it('generates Package.swift + symlinks when headers ship in the slot', () => {
176176
writeAppPkg();
177-
// ReactNativeHeaders present → the headers composer is never consulted.
178177
const artifactsDir = writeArtifacts([
179178
'React',
180179
'ReactNativeDependencies',
181180
'hermes-engine',
182181
'ReactNativeHeaders',
183182
]);
184183
try {
185-
const ensureHeadersLayout = jest.fn();
186-
run(artifactsDir, {ensureHeadersLayout});
184+
run(artifactsDir);
187185

188-
expect(ensureHeadersLayout).not.toHaveBeenCalled();
189186
expect(process.exitCode).toBeUndefined();
190187

191188
const pkgSwift = path.join(
@@ -213,46 +210,27 @@ describe('main', () => {
213210
}
214211
});
215212

216-
it('composes the headers layout when ReactNativeHeaders is absent', () => {
213+
it('errors when ReactNativeHeaders is absent (no consumer-side compose)', () => {
217214
writeAppPkg();
215+
// Artifacts WITHOUT ReactNativeHeaders: the consumer does not compose the
216+
// layout locally, it fails with a clear error instead.
218217
const artifactsDir = writeArtifacts([
219218
'React',
220219
'ReactNativeDependencies',
221220
'hermes-engine',
222221
]);
223-
// Targets the injected composer points the React/headers symlinks at.
224-
const composedReact = path.join(artifactsDir, 'composed-React.xcframework');
225-
const composedHeaders = path.join(
226-
artifactsDir,
227-
'composed-ReactNativeHeaders.xcframework',
228-
);
229-
fs.mkdirSync(composedReact, {recursive: true});
230-
fs.mkdirSync(composedHeaders, {recursive: true});
231222
try {
232-
const ensureHeadersLayout = jest.fn(() => ({
233-
reactXcfw: composedReact,
234-
headersXcfw: composedHeaders,
235-
}));
236-
run(artifactsDir, {ensureHeadersLayout});
237-
238-
expect(ensureHeadersLayout).toHaveBeenCalledTimes(1);
239-
expect(process.exitCode).toBeUndefined();
223+
run(artifactsDir);
240224

241-
const headersLink = path.join(
242-
appRoot,
243-
'build',
244-
'xcframeworks',
245-
'ReactNativeHeaders.xcframework',
246-
);
247-
expect(fs.lstatSync(headersLink).isSymbolicLink()).toBe(true);
248-
expect(fs.readlinkSync(headersLink)).toBe(composedHeaders);
249-
// The React symlink is re-pointed at the composed override, not the raw entry.
225+
expect(process.exitCode).toBe(1);
226+
// No package is generated when the artifacts are incomplete.
250227
expect(
251-
fs.readlinkSync(
252-
path.join(appRoot, 'build', 'xcframeworks', 'React.xcframework'),
228+
fs.existsSync(
229+
path.join(appRoot, 'build', 'xcframeworks', 'Package.swift'),
253230
),
254-
).toBe(composedReact);
231+
).toBe(false);
255232
} finally {
233+
process.exitCode = undefined;
256234
fs.rmSync(artifactsDir, {recursive: true, force: true});
257235
}
258236
});

packages/react-native/scripts/spm/generate-spm-package.js

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,7 @@ ${binaryTargets}
224224
`;
225225
}
226226

227-
function main(
228-
argv /*:: ?: Array<string> */,
229-
deps /*:: ?: {ensureHeadersLayout?: (string, string, string) => {reactXcfw: string, headersXcfw: string}} */,
230-
) /*: void */ {
227+
function main(argv /*:: ?: Array<string> */) /*: void */ {
231228
const args = parseArgs(argv ?? process.argv.slice(2));
232229
// Ensure appRoot is always absolute so path.join/path.resolve produce absolute paths
233230
// even when called with --app-root . or other relative paths.
@@ -307,27 +304,21 @@ function main(
307304
const xcfwLinksDir = path.join(appRoot, 'build', 'xcframeworks');
308305
fs.mkdirSync(xcfwLinksDir, {recursive: true});
309306

310-
// The spec layout (headers-spec.js) is what
311-
// consumers compile against. When the slot's artifacts.json already
312-
// carries ReactNativeHeaders (published or local tarball via
313-
// download-spm-artifacts), the artifacts ship the layout and are used
314-
// as-is. Otherwise the layout is COMPOSED locally from the slot's
315-
// React + deps artifacts (binaries untouched; headers re-projected) —
316-
// fast, marker-cached, re-run safe.
317-
const rnPkgRoot = path.resolve(__dirname, '..', '..');
318-
const overrides /*: {[string]: string} */ = {};
307+
// Consumers compile against the spec layout (headers-spec.js, emitted at
308+
// prebuild time). The prebuilt core artifact must ship React.xcframework AND
309+
// ReactNativeHeaders.xcframework together — published tarballs and
310+
// download-spm-artifacts include both. The layout is NOT composed on the
311+
// consumer side (that needs the full RN repo / ios-prebuild build scripts,
312+
// which the npm package deliberately does not ship).
319313
if (raw.ReactNativeHeaders == null) {
320-
const ensureHeadersLayout =
321-
deps?.ensureHeadersLayout ??
322-
// $FlowFixMe[cannot-resolve-module] cross-dir require into ios-prebuild
323-
require('../ios-prebuild/headers-compose').ensureHeadersLayout;
324-
const composed = ensureHeadersLayout(
325-
artifactsDir ?? path.dirname(String(raw.React?.xcframeworkPath ?? '')),
326-
rnPkgRoot,
327-
path.join(rnPkgRoot, 'build', 'headers'),
314+
console.error(
315+
'Prebuilt artifacts are missing ReactNativeHeaders.xcframework — the ' +
316+
'React Native core artifact must ship it alongside React.xcframework. ' +
317+
'Re-download or rebuild the artifact (or point --artifacts-dir at a ' +
318+
'complete slot).',
328319
);
329-
overrides.React = composed.reactXcfw;
330-
overrides.ReactNativeHeaders = composed.headersXcfw;
320+
process.exitCode = 1;
321+
return;
331322
}
332323

333324
const names /*: Array<string> */ = [];
@@ -346,10 +337,7 @@ function main(
346337
};
347338
// $FlowFixMe[incompatible-use] Object.entries values typed as mixed
348339
for (const [name, entry] of Object.entries(raw)) {
349-
linkOne(name, overrides[name] ?? entry.xcframeworkPath);
350-
}
351-
if (overrides.ReactNativeHeaders != null) {
352-
linkOne('ReactNativeHeaders', overrides.ReactNativeHeaders);
340+
linkOne(name, entry.xcframeworkPath);
353341
}
354342

355343
// Pass the absolute artifacts dir so the binary target paths reference the

0 commit comments

Comments
 (0)