From c2fb47152cae1d0c70e3e1589f2dd87da702c18a Mon Sep 17 00:00:00 2001 From: Christian Falch Date: Thu, 20 Aug 2026 16:00:32 +0200 Subject: [PATCH] Derive the generated SPM manifests from shared name constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The names of React Native's own SPM package and products were spelled out as literals across five emitters, so the xcframeworks manifest, the aggregator, the synth wrappers, the scaffolder and the pbxproj product references each had their own copy of the same list. Adding or renaming a product meant finding all of them. Move the names into spm-utils and tag them by kind — the umbrella Clang target and the xcframework-backed products — so the flat product list is derived rather than repeated, and no emitter has to infer a product's kind from its position in an array. Pure refactor: the generated manifests, the pbxproj product references and the installed codegen template are byte-identical, in local and in remote mode. Co-Authored-By: Claude Opus 5 (1M context) --- .../__tests__/generate-spm-package-test.js | 53 ++++++++++++++++ .../__tests__/generate-spm-xcodeproj-test.js | 49 +++++++++++++++ .../scripts/spm/__tests__/spm-utils-test.js | 63 +++++++++++++++++++ .../scripts/spm/generate-spm-autolinking.js | 34 ++++++---- .../scripts/spm/generate-spm-package.js | 48 ++++++++------ .../scripts/spm/generate-spm-xcodeproj.js | 40 +++++------- .../scripts/spm/scaffold-package-swift.js | 31 +++++---- .../react-native/scripts/spm/spm-utils.js | 63 ++++++++++++++++++- 8 files changed, 310 insertions(+), 71 deletions(-) diff --git a/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js b/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js index f32559e234ef..2a6e02937273 100644 --- a/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js +++ b/packages/react-native/scripts/spm/__tests__/generate-spm-package-test.js @@ -23,6 +23,59 @@ const path = require('node:path'); // generateXCFrameworksPackageSwift // --------------------------------------------------------------------------- +// Adding a product must be one edit to REACT_NATIVE_PRODUCTS (plus the static +// codegen template): the manifest's products AND targets both derive from it. +// ReactHeaders is the Clang umbrella target; every other product is served by +// an xcframework of the same name. +describe('generateXCFrameworksPackageSwift derives from the shared name constants', () => { + // jest.doMock registers in the module registry beyond the isolateModules + // scope, so the mocked constants must be dropped before the next test. + afterEach(() => { + jest.dontMock('../spm-utils'); + jest.resetModules(); + }); + + it('emits a product and a binary target for a newly reserved product', () => { + jest.isolateModules(() => { + // Declared by KIND, the way a real edit adds one — spm-utils derives the + // flat list from the kind lists, so the mock mirrors that derivation. + jest.doMock('../spm-utils', () => { + const actual = jest.requireActual('../spm-utils'); + const xcframeworkProducts = Object.freeze([ + ...actual.REACT_NATIVE_XCFRAMEWORK_PRODUCTS, + 'ReactBrandNewHeaders', + ]); + return { + ...actual, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS: xcframeworkProducts, + REACT_NATIVE_PRODUCTS: Object.freeze([ + actual.REACT_NATIVE_UMBRELLA_PRODUCT, + ...xcframeworkProducts, + ]), + }; + }); + const { + generateXCFrameworksPackageSwift: generate, + } = require('../generate-spm-package'); + const out = generate(); + expect(out).toContain( + '.library(name: "ReactBrandNewHeaders", targets: ["ReactBrandNewHeaders"])', + ); + expect(out).toContain('path: "ReactBrandNewHeaders.xcframework"'); + }); + }); + + it('emits one library product per REACT_NATIVE_PRODUCTS entry, in order', () => { + const {REACT_NATIVE_PRODUCTS} = require('../spm-utils'); + const libraries = [ + ...generateXCFrameworksPackageSwift().matchAll( + /\.library\(name: "([^"]+)"/g, + ), + ].map(m => m[1]); + expect(libraries).toEqual([...REACT_NATIVE_PRODUCTS]); + }); +}); + describe('generateXCFrameworksPackageSwift', () => { it('exposes only invariant compile-time products', () => { const result = generateXCFrameworksPackageSwift(); diff --git a/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js b/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js index 34af3d715487..47c1326a2cf6 100644 --- a/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js +++ b/packages/react-native/scripts/spm/__tests__/generate-spm-xcodeproj-test.js @@ -61,6 +61,55 @@ const FRAMEWORK = { ], }; +// The pbxproj product references must follow the shared name constants: a +// product added there has to reach the app target, or the app links against a +// package product Xcode never references. +describe('SPM product references derive from the shared name constants', () => { + // jest.doMock registers in the module registry beyond the isolateModules + // scope, so the mocked constants must be dropped before the next test. + afterEach(() => { + jest.dontMock('../spm-utils'); + jest.resetModules(); + }); + + it('includes a newly reserved React Native product', () => { + jest.isolateModules(() => { + jest.doMock('../spm-utils', () => { + const actual = jest.requireActual('../spm-utils'); + return { + ...actual, + REACT_NATIVE_PRODUCTS: Object.freeze([ + ...actual.REACT_NATIVE_PRODUCTS, + 'ReactBrandNewHeaders', + ]), + }; + }); + const {buildSpmDependencyGraph} = require('../generate-spm-xcodeproj'); + const graph = buildSpmDependencyGraph( + (section, id) => `${section}:${id}`, + ); + expect(graph.products.map(p => p.product)).toContain( + 'ReactBrandNewHeaders', + ); + }); + }); + + it('references every React Native, aggregator and codegen product exactly once', () => { + const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_APP_PRODUCTS, + REACT_NATIVE_PRODUCTS, + } = require('../spm-utils'); + const {buildSpmDependencyGraph} = require('../generate-spm-xcodeproj'); + const graph = buildSpmDependencyGraph((section, id) => `${section}:${id}`); + expect(graph.products.map(p => p.product)).toEqual([ + ...REACT_NATIVE_PRODUCTS, + AUTOLINKED_PACKAGE_NAME, + ...REACT_CODEGEN_APP_PRODUCTS, + ]); + }); +}); + describe('scheme pre-action', () => { it('contains the sync script and target-scoped build environment', () => { const result = generateXcscheme( diff --git a/packages/react-native/scripts/spm/__tests__/spm-utils-test.js b/packages/react-native/scripts/spm/__tests__/spm-utils-test.js index f378a1d6ca1c..f50bf4c2f9dd 100644 --- a/packages/react-native/scripts/spm/__tests__/spm-utils-test.js +++ b/packages/react-native/scripts/spm/__tests__/spm-utils-test.js @@ -11,6 +11,16 @@ 'use strict'; const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_APP_PRODUCTS, + REACT_CODEGEN_PACKAGE_NAME, + REACT_CODEGEN_PRODUCTS, + REACT_HEADERS_TARGET_DIR, + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, + REACT_NATIVE_UMBRELLA_PRODUCT, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS, RemoteVersionError, buildPerAppHeaderTree, defaultCacheDir, @@ -46,6 +56,59 @@ describe('toSwiftName', () => { }); }); +// --------------------------------------------------------------------------- +// Name constants — the single list every generated manifest derives its +// package and product names from +// --------------------------------------------------------------------------- + +describe('name constants', () => { + it('names the React Native package and the per-app codegen package', () => { + expect(REACT_NATIVE_PACKAGE_NAME).toBe('ReactNative'); + expect(REACT_CODEGEN_PACKAGE_NAME).toBe('React-GeneratedCode'); + }); + + it('pins each product list to its literal names', () => { + expect(REACT_NATIVE_PRODUCTS).toEqual([ + 'ReactHeaders', + 'ReactNativeHeaders', + 'ReactNativeDependenciesHeaders', + ]); + expect(REACT_CODEGEN_PRODUCTS).toEqual(['ReactAppHeaders']); + expect(REACT_CODEGEN_APP_PRODUCTS).toEqual([ + 'ReactCodegen', + 'ReactAppDependencyProvider', + ]); + }); + + it('tags each React Native product by kind, so no consumer has to infer it from position', () => { + expect(REACT_NATIVE_UMBRELLA_PRODUCT).toBe('ReactHeaders'); + expect(REACT_NATIVE_HEADERS_PRODUCT).toBe('ReactNativeHeaders'); + expect(REACT_NATIVE_XCFRAMEWORK_PRODUCTS).toEqual([ + 'ReactNativeHeaders', + 'ReactNativeDependenciesHeaders', + ]); + }); + + it('names the autolinking aggregator package (which shares its name with its product)', () => { + expect(AUTOLINKED_PACKAGE_NAME).toBe('Autolinked'); + }); + + it('names the invariant React headers target directory', () => { + expect(REACT_HEADERS_TARGET_DIR).toBe('ReactHeadersTarget'); + }); + + it('freezes the lists so no caller can mutate the shared source of truth', () => { + for (const list of [ + REACT_NATIVE_PRODUCTS, + REACT_CODEGEN_PRODUCTS, + REACT_CODEGEN_APP_PRODUCTS, + ]) { + expect(Array.isArray(list)).toBe(true); + expect(Object.isFrozen(list)).toBe(true); + } + }); +}); + // --------------------------------------------------------------------------- // defaultCacheDir // --------------------------------------------------------------------------- diff --git a/packages/react-native/scripts/spm/generate-spm-autolinking.js b/packages/react-native/scripts/spm/generate-spm-autolinking.js index 5e933af01443..cf2932731a48 100644 --- a/packages/react-native/scripts/spm/generate-spm-autolinking.js +++ b/packages/react-native/scripts/spm/generate-spm-autolinking.js @@ -65,6 +65,11 @@ const { } = require('./expand-spm-dependencies'); const {readPodspec} = require('./read-podspec'); const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_PACKAGE_NAME, + REACT_CODEGEN_PRODUCTS, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, RemoteVersionError, findProjectRoot, makeLogger, @@ -90,7 +95,7 @@ const {log, warn} = makeLogger('generate-spm-autolinking'); let remoteCfg /*: ?{url: string, version: string, identity: string} */ = null; function reactNativePackageLabel() /*: string */ { - return remoteCfg != null ? remoteCfg.identity : 'ReactNative'; + return remoteCfg != null ? remoteCfg.identity : REACT_NATIVE_PACKAGE_NAME; } function reactNativePackageDecl(localDecl /*: string */) /*: string */ { return remoteCfg != null @@ -105,10 +110,11 @@ function reactNativePackageDecl(localDecl /*: string */) /*: string */ { function reactProducts() /*: Array<{name: string, package: string}> */ { const rn = reactNativePackageLabel(); return [ - {name: 'ReactHeaders', package: rn}, - {name: 'ReactNativeHeaders', package: rn}, - {name: 'ReactNativeDependenciesHeaders', package: rn}, - {name: 'ReactAppHeaders', package: 'React-GeneratedCode'}, + ...REACT_NATIVE_PRODUCTS.map(name => ({name, package: rn})), + ...REACT_CODEGEN_PRODUCTS.map(name => ({ + name, + package: REACT_CODEGEN_PACKAGE_NAME, + })), ]; } function reactProductDeps() /*: string */ { @@ -147,7 +153,7 @@ function reactDescriptor( }; } else if (absXcframeworks != null) { packageRef = { - name: 'ReactNative', + name: REACT_NATIVE_PACKAGE_NAME, path: toPosix(absXcframeworks), relPath: xcframeworksRelPath != null ? toPosix(xcframeworksRelPath) : undefined, @@ -156,7 +162,7 @@ function reactDescriptor( return null; } const products = reactProducts().filter( - p => p.package !== 'React-GeneratedCode' || codegenPackageExists, + p => p.package !== REACT_CODEGEN_PACKAGE_NAME || codegenPackageExists, ); return {packageRef, products}; } @@ -900,12 +906,14 @@ function generateAutolinkedPackageSwift( ) { packageDeps.push( reactNativePackageDecl( - `.package(name: "ReactNative", path: "${xcframeworksRelPath}")`, + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${xcframeworksRelPath}")`, ), ); // Per-app generated headers come from the ReactAppHeaders product in // the codegen package (sibling of the autolinking dir). - packageDeps.push(`.package(name: "React-GeneratedCode", path: "../ios")`); + packageDeps.push( + `.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "../ios")`, + ); } // AutolinkedAggregate's target dependencies: .product(...) for npm sub-package @@ -1008,10 +1016,10 @@ import PackageDescription import Foundation ${guardBlock}let package = Package( - name: "Autolinked", + name: "${AUTOLINKED_PACKAGE_NAME}", platforms: [.iOS(.v15)], products: [ - .library(name: "Autolinked", targets: ["AutolinkedAggregate"]), + .library(name: "${AUTOLINKED_PACKAGE_NAME}", targets: ["AutolinkedAggregate"]), ], ${packageDepsBlock} targets: [ .target( @@ -1075,13 +1083,13 @@ function generateSynthPackageSwift(spec /*: SynthPackageSpec */) /*: string */ { spec.codegenPackagePath ?? '../../../ios'; packageDeps.push( reactNativePackageDecl( - `.package(name: "ReactNative", path: "${reactNativePackagePath}")`, + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${reactNativePackagePath}")`, ), ); // Per-app generated headers come from the ReactAppHeaders product in // the codegen package. packageDeps.push( - `.package(name: "React-GeneratedCode", path: "${codegenPackagePath}")`, + `.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "${codegenPackagePath}")`, ); } for (const dep of spmDependencies) { diff --git a/packages/react-native/scripts/spm/generate-spm-package.js b/packages/react-native/scripts/spm/generate-spm-package.js index 058d3ecf0db2..46a12fdb1737 100644 --- a/packages/react-native/scripts/spm/generate-spm-package.js +++ b/packages/react-native/scripts/spm/generate-spm-package.js @@ -37,6 +37,12 @@ const {prepareFlavoredFrameworks} = require('./flavored-frameworks'); const { + REACT_HEADERS_TARGET_DIR, + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, + REACT_NATIVE_UMBRELLA_PRODUCT, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS, deriveAppName, displayPath, findProjectRoot, @@ -166,32 +172,38 @@ function findSourcePath( * Package.swift also imports it as a named package dependency. */ function generateXCFrameworksPackageSwift() /*: string */ { + // Each product's target follows from its KIND, not from its position in the + // list: the umbrella is a Clang target over the staged headers, and every + // xcframework-backed product gets a binaryTarget of the same name. + const products = REACT_NATIVE_PRODUCTS.map( + product => ` .library(name: "${product}", targets: ["${product}"]),`, + ); + const targets = [ + ` .target( + name: "${REACT_NATIVE_UMBRELLA_PRODUCT}", + dependencies: ["${REACT_NATIVE_HEADERS_PRODUCT}"], + path: "${REACT_HEADERS_TARGET_DIR}", + publicHeadersPath: "include" + ),`, + ...REACT_NATIVE_XCFRAMEWORK_PRODUCTS.map( + product => ` .binaryTarget( + name: "${product}", + path: "${product}.xcframework" + ),`, + ), + ]; + return `// swift-tools-version: 6.0 // AUTO-GENERATED by scripts/generate-spm-package.js – do not edit manually. import PackageDescription let package = Package( - name: "ReactNative", + name: "${REACT_NATIVE_PACKAGE_NAME}", products: [ - .library(name: "ReactHeaders", targets: ["ReactHeaders"]), - .library(name: "ReactNativeHeaders", targets: ["ReactNativeHeaders"]), - .library(name: "ReactNativeDependenciesHeaders", targets: ["ReactNativeDependenciesHeaders"]), +${products.join('\n')} ], targets: [ - .target( - name: "ReactHeaders", - dependencies: ["ReactNativeHeaders"], - path: "ReactHeadersTarget", - publicHeadersPath: "include" - ), - .binaryTarget( - name: "ReactNativeHeaders", - path: "ReactNativeHeaders.xcframework" - ), - .binaryTarget( - name: "ReactNativeDependenciesHeaders", - path: "ReactNativeDependenciesHeaders.xcframework" - ), +${targets.join('\n')} ] ) `; diff --git a/packages/react-native/scripts/spm/generate-spm-xcodeproj.js b/packages/react-native/scripts/spm/generate-spm-xcodeproj.js index a32b57650a8d..5c0c5d35ce33 100644 --- a/packages/react-native/scripts/spm/generate-spm-xcodeproj.js +++ b/packages/react-native/scripts/spm/generate-spm-xcodeproj.js @@ -46,6 +46,11 @@ const { uuidComment, } = require('./spm-pbxproj'); const { + AUTOLINKED_PACKAGE_NAME, + REACT_CODEGEN_APP_PRODUCTS, + REACT_CODEGEN_PACKAGE_NAME, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, isValidScriptPhaseId, isValidScriptPhaseName, makeLogger, @@ -111,36 +116,21 @@ const GENERATED_SOURCE_FILE_TYPES /*: {[string]: string} */ = { // resolve the product dependencies — SPM doesn't expose transitive products. const SPM_PRODUCT_PACKAGES /*: Array<{product: string, packagePath: string, packageName: string}> */ = [ - { - product: 'ReactHeaders', - packagePath: 'build/xcframeworks', - packageName: 'ReactNative', - }, - { - product: 'ReactNativeHeaders', + ...REACT_NATIVE_PRODUCTS.map(product => ({ + product, packagePath: 'build/xcframeworks', - packageName: 'ReactNative', - }, + packageName: REACT_NATIVE_PACKAGE_NAME, + })), { - product: 'ReactNativeDependenciesHeaders', - packagePath: 'build/xcframeworks', - packageName: 'ReactNative', - }, - { - product: 'Autolinked', + product: AUTOLINKED_PACKAGE_NAME, packagePath: 'build/generated/autolinking', - packageName: 'Autolinked', - }, - { - product: 'ReactCodegen', - packagePath: 'build/generated/ios', - packageName: 'React-GeneratedCode', + packageName: AUTOLINKED_PACKAGE_NAME, }, - { - product: 'ReactAppDependencyProvider', + ...REACT_CODEGEN_APP_PRODUCTS.map(product => ({ + product, packagePath: 'build/generated/ios', - packageName: 'React-GeneratedCode', - }, + packageName: REACT_CODEGEN_PACKAGE_NAME, + })), ]; /*:: diff --git a/packages/react-native/scripts/spm/scaffold-package-swift.js b/packages/react-native/scripts/spm/scaffold-package-swift.js index 6d52f60760b8..ce3103ebedda 100644 --- a/packages/react-native/scripts/spm/scaffold-package-swift.js +++ b/packages/react-native/scripts/spm/scaffold-package-swift.js @@ -43,6 +43,10 @@ const { const {expandSpmSourceGlobs} = require('./generate-spm-autolinking'); const {readPodspec} = require('./read-podspec'); const { + REACT_CODEGEN_PACKAGE_NAME, + REACT_CODEGEN_PRODUCTS, + REACT_NATIVE_PACKAGE_NAME, + REACT_NATIVE_PRODUCTS, SCAFFOLDER_MARKER, makeLogger, remotePackageConfig, @@ -652,7 +656,8 @@ function emitScaffoldedPackageSwift( // the app by definition, so it stays a path reference — relative, // computed at scaffold time. const remote = ctx.remote; - const rnLabel = remote != null ? remote.identity : 'ReactNative'; + const rnLabel = + remote != null ? remote.identity : REACT_NATIVE_PACKAGE_NAME; const codegenDir = ctx.codegenPackageDir; if (codegenDir == null) { throw new Error( @@ -670,21 +675,21 @@ function emitScaffoldedPackageSwift( 'emitScaffoldedPackageSwift: localXcfwPackageDir is required when no remote package is configured.', ); } - packageDeps.push(`.package(name: "ReactNative", path: "${xcfwDir}")`); + packageDeps.push( + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${xcfwDir}")`, + ); } packageDeps.push( - `.package(name: "React-GeneratedCode", path: "${codegenDir}")`, - ); - targetDeps.push(`.product(name: "ReactHeaders", package: "${rnLabel}")`); - targetDeps.push( - `.product(name: "ReactNativeHeaders", package: "${rnLabel}")`, - ); - targetDeps.push( - `.product(name: "ReactNativeDependenciesHeaders", package: "${rnLabel}")`, - ); - targetDeps.push( - '.product(name: "ReactAppHeaders", package: "React-GeneratedCode")', + `.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "${codegenDir}")`, ); + for (const product of REACT_NATIVE_PRODUCTS) { + targetDeps.push(`.product(name: "${product}", package: "${rnLabel}")`); + } + for (const product of REACT_CODEGEN_PRODUCTS) { + targetDeps.push( + `.product(name: "${product}", package: "${REACT_CODEGEN_PACKAGE_NAME}")`, + ); + } } for (const siblingName of spec.siblingNames) { const swiftSibling = toSwiftName(siblingName); diff --git a/packages/react-native/scripts/spm/spm-utils.js b/packages/react-native/scripts/spm/spm-utils.js index 68a70b20133d..c974b12b01f6 100644 --- a/packages/react-native/scripts/spm/spm-utils.js +++ b/packages/react-native/scripts/spm/spm-utils.js @@ -14,6 +14,55 @@ const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); +// The package and product names React Native's own generated manifests use, +// in one place so the emitters in this directory cannot drift apart from each +// other. +// +// Adding a React Native SPM product also touches, depending on what the +// product is: +// - scripts/codegen/templates/Package.swift.spm-template — if the per-app +// codegen package must depend on it (a static Swift file, patched by +// string replacement, not generated from these constants) +// - flavored-frameworks.js INVARIANT_BINARY_TARGETS — if it is +// xcframework-backed +// - download-spm-artifacts.js REQUIRED_ARTIFACTS — if it ships as its own +// downloadable artifact (that list names ARTIFACTS, which overlap with +// product names without being the same set) +const REACT_NATIVE_PACKAGE_NAME /*: string */ = 'ReactNative'; +const REACT_CODEGEN_PACKAGE_NAME /*: string */ = 'React-GeneratedCode'; +// The autolinking aggregator package, whose single product shares its name. +const AUTOLINKED_PACKAGE_NAME /*: string */ = 'Autolinked'; +// React Native's products by KIND, so no consumer has to infer the kind from a +// position in a flat list. The umbrella is a Clang target over the staged +// headers that re-exports the pure-RN namespaces; the rest are each backed by +// an xcframework of the same name. +const REACT_NATIVE_UMBRELLA_PRODUCT /*: string */ = 'ReactHeaders'; +const REACT_NATIVE_HEADERS_PRODUCT /*: string */ = 'ReactNativeHeaders'; +const REACT_NATIVE_DEPENDENCIES_HEADERS_PRODUCT /*: string */ = + 'ReactNativeDependenciesHeaders'; +const REACT_NATIVE_XCFRAMEWORK_PRODUCTS /*: ReadonlyArray */ = + Object.freeze([ + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_DEPENDENCIES_HEADERS_PRODUCT, + ]); +// Derived, so a new product cannot be added without choosing a kind above. +const REACT_NATIVE_PRODUCTS /*: ReadonlyArray */ = Object.freeze([ + REACT_NATIVE_UMBRELLA_PRODUCT, + ...REACT_NATIVE_XCFRAMEWORK_PRODUCTS, +]); +// The codegen package product every autolinked target depends on for the app's +// generated headers. +const REACT_CODEGEN_PRODUCTS /*: ReadonlyArray */ = Object.freeze([ + 'ReactAppHeaders', +]); +// The codegen package products the APP target links, declared by the static +// template rather than by any emitter here. +const REACT_CODEGEN_APP_PRODUCTS /*: ReadonlyArray */ = Object.freeze([ + 'ReactCodegen', + 'ReactAppDependencyProvider', +]); +const REACT_HEADERS_TARGET_DIR /*: string */ = 'ReactHeadersTarget'; + /** * Creates a logger trio {log, warn, die} that prefixes messages with [name]. * log – green prefix, writes to stdout @@ -582,10 +631,10 @@ function installSpmCodegenTemplate( if (remote != null) { content = content .replace( - '.package(name: "ReactNative", path: "../../xcframeworks"),', + `.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "../../xcframeworks"),`, `.package(url: "${remote.url}", exact: "${remote.version}"),`, ) - .split('package: "ReactNative")') + .split(`package: "${REACT_NATIVE_PACKAGE_NAME}")`) .join(`package: "${remote.identity}")`); } fs.writeFileSync(codegenPkgSwift, content, 'utf8'); @@ -666,6 +715,16 @@ function isValidScriptPhaseName(value /*: unknown */) /*: boolean */ { } module.exports = { + REACT_NATIVE_PACKAGE_NAME, + REACT_CODEGEN_PACKAGE_NAME, + AUTOLINKED_PACKAGE_NAME, + REACT_NATIVE_UMBRELLA_PRODUCT, + REACT_NATIVE_HEADERS_PRODUCT, + REACT_NATIVE_XCFRAMEWORK_PRODUCTS, + REACT_NATIVE_PRODUCTS, + REACT_CODEGEN_PRODUCTS, + REACT_CODEGEN_APP_PRODUCTS, + REACT_HEADERS_TARGET_DIR, makeLogger, displayPath, sharedCacheDir,