UNPKG

react-native

Version:

A framework for building native apps using React

170 lines (155 loc) • 5.89 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict-local * @format */ 'use strict'; /** * sync-spm-autolinking.js – Lightweight script invoked by the Xcode pre-build * phase to re-run invariant codegen and autolinking output when dependency * inputs have changed. * * Usage (called from the Xcode build phase shell script): * node sync-spm-autolinking.js --app-root <path> --react-native-root <path> * * The autolinking config (autolinking.json) is generated by the caller * (setup-apple-spm.js) BEFORE this script runs, so codegen below can reuse it * (findCodegenEnabledLibraries short-circuits on a present autolinking.json) * and the slow `@react-native-community/cli config` runs exactly once per sync. * * This script: * 0. Runs react-native codegen → build/generated/ios/ (reuses autolinking.json) * 1. Installs the codegen template into build/generated/ios/Package.swift, * replacing codegen's mis-rooted default (done immediately, before any * step below that can throw — see the failure-atomicity comment at the * call site) * 2. Calls generate-spm-autolinking.js → build/generated/autolinking/Package.swift * 3. Rebuilds the generated-headers farm * 4. Writes build/generated/autolinking/.spm-sync-stamp * * Runtime frameworks are deliberately not downloaded or regenerated from an * Xcode build. `spm add` / `spm update` own the immutable dual-flavor slots and * the app target's linker/embed configuration. */ const {main: generateAutolinking} = require('./generate-spm-autolinking'); const { RemoteVersionError, buildPerAppHeaderTree, findProjectRoot, installSpmCodegenTemplate, makeLogger, runCodegenAndInstallTemplate, } = require('./spm-utils'); const fs = require('node:fs'); const path = require('node:path'); const yargs = require('yargs'); const {log} = makeLogger('sync-spm-autolinking'); // Collaborators are injected (with these real implementations as defaults) so // main() can be exercised end-to-end in tests without mocking the module // system. Tests pass fakes that record calls and point defaultCacheDir at a // tempdir; everything fs-based runs for real against that tempdir. const defaultDeps = { runCodegenAndInstallTemplate, generateAutolinking, installSpmCodegenTemplate, buildPerAppHeaderTree, findProjectRoot, }; async function main( argv /*:: ?: Array<string> */, overrides /*:: ?: Partial<typeof defaultDeps> */, ) /*: Promise<void> */ { const deps = {...defaultDeps, ...(overrides ?? {})}; const parsed = yargs(argv ?? process.argv.slice(2)) .version(false) .option('app-root', { type: 'string', demandOption: true, describe: 'Path to the app directory', }) .option('react-native-root', { type: 'string', demandOption: true, describe: 'Path to react-native package root', }) .option('ios-deployment-target', { type: 'string', describe: 'Platform floor forwarded to the autolinker', }) .help() .parseSync(); const appRoot = path.resolve(parsed['app-root']); const reactNativeRoot = path.resolve(parsed['react-native-root']); const projectRoot = deps.findProjectRoot(appRoot); // The caller (setup-apple-spm.js) already generated autolinking.json before // invoking this script, so codegen reuses it here. try { deps.runCodegenAndInstallTemplate( projectRoot, appRoot, reactNativeRoot, {log}, { installTemplate: false, }, ); } catch { log('Codegen failed — continuing with existing output'); } // Install the codegen template right away, before artifact download and // autolinking-plugin execution below. Failure-atomicity: generateAutolinking // runs fail-closed plugins that can throw, and if that happens before the // template is installed, codegen's mis-rooted default manifest is left in // build/generated/ios/Package.swift — breaking every subsequent Xcode // "Resolve Package Graph". Installing it immediately after codegen means a // later throw can never leave that broken manifest in place. deps.installSpmCodegenTemplate(appRoot, reactNativeRoot, {log}); log('Re-generating build/generated/autolinking/Package.swift...'); const autolinkingArgv = [ '--app-root', appRoot, '--react-native-root', reactNativeRoot, ]; const iosDeploymentTarget = parsed['ios-deployment-target']; if (iosDeploymentTarget != null) { autolinkingArgv.push('--ios-deployment-target', iosDeploymentTarget); } deps.generateAutolinking(autolinkingArgv); // Rebuild the per-app generated-headers farm (vended as the ReactAppHeaders // SPM target inside the codegen package). React core headers need no trees // — they are vended by the invariant ReactHeaders/ReactNativeHeaders products. // The // generated manifests are fully declarative (fixed-relative package paths), // so no path-locator JSON is written. deps.buildPerAppHeaderTree(appRoot, {log}); const stampPath = path.join( appRoot, 'build', 'generated', 'autolinking', '.spm-sync-stamp', ); fs.mkdirSync(path.dirname(stampPath), {recursive: true}); fs.writeFileSync(stampPath, new Date().toISOString() + '\n', 'utf8'); log('SPM autolinking sync complete.'); } if (require.main === module) { main().catch(e => { if (e instanceof RemoteVersionError) { // Clean message + exit 2 (the build phase hard-fails on it) instead of a // stack trace, matching how setup-apple-spm.js surfaces remote-mode // version errors. log(e.message); process.exitCode = 2; return; } console.error(e); process.exitCode = 1; }); } module.exports = {main};