react-native 0.87.0-rc.0 → 0.87.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/Libraries/Core/InitializeCore.js +9 -1
  2. package/Libraries/Core/ReactNativeVersion.js +1 -1
  3. package/Libraries/ReactNative/AppRegistry.flow.js +1 -0
  4. package/Libraries/ReactNative/AppRegistryImpl.js +2 -3
  5. package/Libraries/ReactPrivate/ReactNativePrivateInterface.js +5 -116
  6. package/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow +5 -31
  7. package/React/Base/RCTVersion.m +1 -1
  8. package/React/I18n/RCTLocalizedString.mm +38 -2
  9. package/React-Core-prebuilt.podspec +45 -17
  10. package/React-Core.podspec +9 -2
  11. package/ReactAndroid/external-artifacts/build.gradle.kts +49 -0
  12. package/ReactAndroid/gradle.properties +1 -1
  13. package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SynchronousMountItem.kt +8 -2
  14. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  15. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  16. package/index.js +15 -3
  17. package/index.js.flow +0 -2
  18. package/package.json +27 -29
  19. package/react-native.config.js +81 -0
  20. package/scripts/cocoapods/fabric.rb +1 -1
  21. package/scripts/cocoapods/rncore.rb +35 -78
  22. package/scripts/cocoapods/rncore_facades.rb +232 -0
  23. package/scripts/cocoapods/rndependencies.rb +64 -3
  24. package/scripts/cocoapods/rndeps_facades.rb +193 -0
  25. package/scripts/cocoapods/spm.rb +78 -11
  26. package/scripts/codegen/templates/Package.swift.spm-template +97 -0
  27. package/scripts/react-native-xcode.sh +20 -0
  28. package/scripts/react_native_pods.rb +65 -16
  29. package/scripts/replace-rncore-version.js +53 -6
  30. package/scripts/setup-apple-spm.js +1165 -0
  31. package/scripts/spm/__doc__/rfc-spm-xcframework.md +707 -0
  32. package/scripts/spm/__doc__/spm-autolinking-plugins.md +244 -0
  33. package/scripts/spm/__doc__/spm-header-paths-contract.md +97 -0
  34. package/scripts/spm/__doc__/spm-plugins-assessment.md +128 -0
  35. package/scripts/spm/__doc__/spm-scripts.md +451 -0
  36. package/scripts/spm/autolinking-plugins.js +331 -0
  37. package/scripts/spm/download-spm-artifacts.js +1409 -0
  38. package/scripts/spm/expand-spm-dependencies.js +216 -0
  39. package/scripts/spm/flavored-frameworks.js +1008 -0
  40. package/scripts/spm/generate-spm-autolinking-config.js +161 -0
  41. package/scripts/spm/generate-spm-autolinking.js +1888 -0
  42. package/scripts/spm/generate-spm-package.js +302 -0
  43. package/scripts/spm/generate-spm-xcodeproj.js +2224 -0
  44. package/scripts/spm/read-podspec.js +695 -0
  45. package/scripts/spm/scaffold-package-swift.js +1206 -0
  46. package/scripts/spm/spm-pbxproj.js +654 -0
  47. package/scripts/spm/spm-types.js +517 -0
  48. package/scripts/spm/spm-utils.js +645 -0
  49. package/scripts/spm/sync-spm-autolinking.js +160 -0
  50. package/sdks/.hermesv1version +1 -0
  51. package/sdks/hermes-engine/utils/replace_hermes_version.js +18 -4
  52. package/sdks/hermes-engine/version.properties +1 -1
  53. package/src/asset-registry.js +1 -1
  54. package/src/react-private-interface.js +145 -0
  55. package/src/react-private-interface.js.flow +48 -0
  56. package/src/setup-env.js +22 -0
  57. package/src/unstable-internals-do-not-use.d.ts +214 -0
  58. package/src/unstable-internals-do-not-use.js +76 -0
  59. package/third-party-podspecs/ReactNativeDependencies.podspec +2 -2
  60. package/types_generated/Libraries/ReactNative/AppRegistry.flow.d.ts +2 -2
  61. package/types_generated/Libraries/ReactPrivate/ReactNativePrivateInterface.d.ts +6 -21
  62. package/types_generated/index.d.ts +1 -2
  63. package/types_generated/src/private/renderer/events/dispatchNativeEvent.d.ts +26 -0
  64. package/types_generated/src/react-private-interface.d.ts +33 -0
  65. package/Libraries/Utilities/SceneTracker.js +0 -42
  66. package/jest-preset.js +0 -26
  67. package/rn-get-polyfills.js +0 -13
  68. package/types/tsconfig.json +0 -17
  69. package/types_generated/Libraries/Components/Touchable/Touchable.d.ts +0 -261
  70. package/types_generated/tsconfig.test.json +0 -17
@@ -6,6 +6,7 @@
6
6
  require "json"
7
7
  require 'net/http'
8
8
  require 'rexml/document'
9
+ require 'shellwords'
9
10
 
10
11
  require_relative './utils.rb'
11
12
 
@@ -36,7 +37,7 @@ def add_rn_third_party_dependencies(s)
36
37
  header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
37
38
 
38
39
  if header_search_paths.is_a?(String)
39
- header_search_paths = header_search_paths.split(" ")
40
+ header_search_paths = Shellwords.shellsplit(header_search_paths)
40
41
  end
41
42
 
42
43
  header_search_paths << "$(PODS_ROOT)/glog"
@@ -47,10 +48,24 @@ def add_rn_third_party_dependencies(s)
47
48
  header_search_paths << "$(PODS_ROOT)/SocketRocket"
48
49
  header_search_paths << "$(PODS_ROOT)/RCT-Folly"
49
50
 
50
- current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths
51
+ # uniq so a second call on the same spec can't duplicate entries.
52
+ current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
51
53
  else
54
+ # Prebuilt-deps mode: this pod SELF-SERVES the third-party headers from its
55
+ # own xcframework (incl. SocketRocket - sole supplier in this mode). See
56
+ # scripts/cocoapods/__docs__/prebuilt-deps.md for the full contract.
52
57
  s.dependency "ReactNativeDependencies"
53
- current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] ||= [] << "$(PODS_ROOT)/ReactNativeDependencies"
58
+
59
+ header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
60
+ if header_search_paths.is_a?(String)
61
+ header_search_paths = Shellwords.shellsplit(header_search_paths)
62
+ end
63
+ # Artifact headers are flattened into the pod-local Headers/ by the podspec
64
+ # prepare_command (see __docs__/prebuilt-deps.md).
65
+ header_search_paths << "$(PODS_ROOT)/ReactNativeDependencies/Headers"
66
+
67
+ # uniq so a second call on the same spec can't duplicate entries.
68
+ current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
54
69
  end
55
70
 
56
71
  s.pod_target_xcconfig = current_pod_target_xcconfig
@@ -139,6 +154,52 @@ class ReactNativeDependenciesUtils
139
154
  end
140
155
  end
141
156
 
157
+ # Single post-install injection site for the prebuilt deps header resolution.
158
+ # Adds the flattened ReactNativeDependencies/Headers search path to the
159
+ # aggregate (main app) target AND every pod target, mirroring
160
+ # ReactNativeCoreUtils.configure_aggregate_xcconfig. ReactNativeHeaders is
161
+ # pure-RN, so this path is the single global home of the third-party
162
+ # namespaces (folly/glog/boost/fmt/double-conversion/fast_float/
163
+ # SocketRocket): pods that never call add_rn_third_party_dependencies (nor
164
+ # depend on a facade) still compile RN headers that textually reach
165
+ # <folly/...>. No module-map activation needed — the deps headers are
166
+ # served textually; modules come from the ReactNativeDependencies pod.
167
+ def self.configure_aggregate_xcconfig(installer)
168
+ return if @@build_from_source
169
+
170
+ rndeps_log("Configuring xcconfig for prebuilt React Native Dependencies...")
171
+ headers_search_path = " \"$(PODS_ROOT)/ReactNativeDependencies/Headers\""
172
+
173
+ # Add the header search path to aggregate target xcconfigs (used by the main app target)
174
+ installer.aggregate_targets.each do |aggregate_target|
175
+ aggregate_target.xcconfigs.each do |config_name, config_file|
176
+ ReactNativePodsUtils.add_flag_to_map_with_inheritance(config_file.attributes, "HEADER_SEARCH_PATHS", headers_search_path)
177
+ xcconfig_path = aggregate_target.xcconfig_path(config_name)
178
+ config_file.save_as(xcconfig_path)
179
+ end
180
+ end
181
+
182
+ # Add the header search path to ALL pod targets (for pods that don't go
183
+ # through add_rn_third_party_dependencies)
184
+ installer.pod_targets.each do |pod_target|
185
+ pod_target.build_settings.each do |config_name, build_settings|
186
+ xcconfig_path = pod_target.xcconfig_path(config_name)
187
+ next unless File.exist?(xcconfig_path)
188
+
189
+ xcconfig = Xcodeproj::Config.new(xcconfig_path)
190
+
191
+ # Skip if the deps header search path is already present
192
+ header_search_paths = xcconfig.attributes["HEADER_SEARCH_PATHS"] || ""
193
+ next if header_search_paths.include?("ReactNativeDependencies/Headers")
194
+
195
+ ReactNativePodsUtils.add_flag_to_map_with_inheritance(xcconfig.attributes, "HEADER_SEARCH_PATHS", headers_search_path)
196
+ xcconfig.save_as(xcconfig_path)
197
+ end
198
+ end
199
+
200
+ rndeps_log("Prebuilt deps xcconfig configuration complete")
201
+ end
202
+
142
203
  def self.podspec_source_download_prebuild_release_tarball()
143
204
  # Warn if @@react_native_path is not set
144
205
  if @@react_native_path == ""
@@ -0,0 +1,193 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ require 'json'
7
+ require 'fileutils'
8
+ # Self-contained against require ordering: this module reads
9
+ # Helpers::Constants.socket_rocket_config. react_native_pods.rb normally loads
10
+ # helpers.rb first, but requiring it here (idempotent) removes that implicit
11
+ # dependency. The defined? guard at the use site stays as a backstop.
12
+ require_relative './helpers'
13
+
14
+ # Dependency-only facade podspecs for the third-party deps in prebuilt-deps
15
+ # mode (deps-side analogue of RNCoreFacades). Design + rationale:
16
+ # scripts/cocoapods/__docs__/prebuilt-deps.md
17
+ module RNDepsFacades
18
+ # The name of the umbrella prebuilt-deps pod every facade depends on. Its pod
19
+ # self-serves the third-party headers + carries the binary (see
20
+ # rndependencies.rb, ReactNativeDependencies.podspec).
21
+ DEPS_POD = "ReactNativeDependencies"
22
+
23
+ # pod name => podspec path (relative to the react-native package root), or
24
+ # :synthesized for a pod with no local podspec (SocketRocket). Version +
25
+ # subspecs + default_subspecs are DERIVED from the real podspec where one
26
+ # exists; synthesized entries derive their version from a constant.
27
+ FACADE_PODS = {
28
+ "RCT-Folly" => "third-party-podspecs/RCT-Folly.podspec",
29
+ "glog" => "third-party-podspecs/glog.podspec",
30
+ "boost" => "third-party-podspecs/boost.podspec",
31
+ "DoubleConversion" => "third-party-podspecs/DoubleConversion.podspec",
32
+ "fmt" => "third-party-podspecs/fmt.podspec",
33
+ "fast_float" => "third-party-podspecs/fast_float.podspec",
34
+ "SocketRocket" => :synthesized,
35
+ }
36
+
37
+ # Sub-directory (relative to the install root) that holds the generated
38
+ # deps facades. Kept separate from RNCoreFacades' `build/rncore-facades` so
39
+ # the two families never collide.
40
+ FACADE_RELDIR = File.join("build", "rndeps-facades")
41
+
42
+ # Generates the facade podspecs and returns the base directory holding them.
43
+ # Each facade gets its OWN sub-directory containing a single
44
+ # `<Name>.podspec.json`, so it can be installed as a LOCAL pod via
45
+ # `:path => <dir>` (PathSource uses the spec in place and never downloads
46
+ # `spec.source`). Idempotent; safe to call once per `pod install`.
47
+ #
48
+ # `react_native_path` locates the real third-party podspecs we mirror.
49
+ # version + subspecs + default_subspecs are DERIVED from the real spec (or,
50
+ # for SocketRocket, synthesized from the socket_rocket_config version) so the
51
+ # facade matches the source pod's spec/subspec SHAPE. It is not fully
52
+ # graph-equivalent: every derived subspec depends only on
53
+ # ReactNativeDependencies, so intra-pod subspec deps (e.g. RCT-Folly/Fabric
54
+ # -> RCT-Folly/Default) are not reproduced — harmless here because the deps
55
+ # are all declared explicitly in react_native_pods.rb. NO source_files and
56
+ # NO headers are emitted — the ReactNativeDependencies pod supplies both. A
57
+ # facaded pod whose real podspec can't be read is a hard error (see
58
+ # load_real_spec) — silently shipping an empty facade would hide drift.
59
+ def self.generate(react_native_path, install_root, ios_version)
60
+ abs_base = File.join(install_root.to_s, FACADE_RELDIR)
61
+ FileUtils.mkdir_p(abs_base)
62
+ FACADE_PODS.each do |name, podspec_rel_path|
63
+ dir = File.join(abs_base, name)
64
+ FileUtils.mkdir_p(dir)
65
+
66
+ if podspec_rel_path == :synthesized
67
+ spec = synthesized_spec(name, ios_version)
68
+ else
69
+ podspec_path = File.join(react_native_path.to_s, podspec_rel_path)
70
+ real = load_real_spec(podspec_path, name)
71
+ spec = derived_spec(name, real, ios_version)
72
+ end
73
+
74
+ File.write(File.join(dir, "#{name}.podspec.json"), JSON.pretty_generate(spec))
75
+ end
76
+ abs_base
77
+ end
78
+
79
+ # Facade dir for `<name>`, RELATIVE to the install root — pass to `pod :path =>`.
80
+ # Relative (not absolute) so the path CocoaPods records in Podfile.lock is
81
+ # portable rather than machine-specific.
82
+ def self.facade_path(name)
83
+ File.join(FACADE_RELDIR, name)
84
+ end
85
+
86
+ # Base spec skeleton shared by derived + synthesized facades: dependency-only,
87
+ # no source_files, no headers. Depends solely on ReactNativeDependencies.
88
+ def self.base_spec(name, version, ios_version)
89
+ {
90
+ "name" => name,
91
+ "version" => version,
92
+ "summary" => "Prebuilt facade for #{name} (code + headers live in #{DEPS_POD}).",
93
+ "homepage" => "https://reactnative.dev/",
94
+ "license" => "MIT",
95
+ "authors" => "Meta Platforms, Inc. and its affiliates",
96
+ "platforms" => { "ios" => ios_version },
97
+ # Required podspec attribute, but never fetched: installed as a LOCAL
98
+ # pod (`:path => <dir>`), which uses this spec in place and ships no
99
+ # source_files. Placeholder only.
100
+ "source" => { "git" => "https://github.com/facebook/react-native.git" },
101
+ "dependencies" => { DEPS_POD => [] },
102
+ }
103
+ end
104
+ private_class_method :base_spec
105
+
106
+ # Facade derived from a real third-party podspec: version + subspecs +
107
+ # default_subspecs mirror the real spec so a bare `pod '<Name>'` and any
108
+ # `pod '<Name>/<Subspec>'` resolve to the SAME graph (e.g. RCT-Folly's
109
+ # bare + /Default + /Fabric). Each subspec is also dependency-only and
110
+ # depends on ReactNativeDependencies.
111
+ #
112
+ # NOTE: resources (e.g. RCT-Folly's PrivacyInfo.xcprivacy) are intentionally
113
+ # NOT carried. In prebuilt-deps mode the third-party code — and its privacy
114
+ # manifest — is embedded in the ReactNativeDependencies artifact; the facade
115
+ # only needs to declare the dependency (see the design note in the PR).
116
+ def self.derived_spec(name, real, ios_version)
117
+ spec = base_spec(name, real.version.to_s, ios_version)
118
+
119
+ defaults = Array(real.default_subspecs)
120
+ spec["default_subspecs"] = defaults unless defaults.empty?
121
+
122
+ subspecs = derive_subspecs(real)
123
+ unless subspecs.empty?
124
+ spec["subspecs"] = subspecs.map do |ss|
125
+ { "name" => ss, "dependencies" => { DEPS_POD => [] } }
126
+ end
127
+ end
128
+
129
+ spec
130
+ end
131
+ private_class_method :derived_spec
132
+
133
+ # Facade synthesized for a pod with NO local podspec (SocketRocket, a trunk
134
+ # pod). Version comes from Helpers::Constants::socket_rocket_config; no
135
+ # subspecs. A missing/blank constant is a hard error rather than a silent
136
+ # versionless facade — a bare `pod 'SocketRocket'` in the source path is
137
+ # `"~> #{socket_rocket_config[:version]}"`, so the facade MUST carry a version
138
+ # that satisfies that constraint.
139
+ def self.synthesized_spec(name, ios_version)
140
+ version = synthesized_version(name)
141
+ base_spec(name, version, ios_version)
142
+ end
143
+ private_class_method :synthesized_spec
144
+
145
+ # Resolves the synthesized version for a no-podspec facade. Fail-closed on a
146
+ # missing constant/version. Only SocketRocket is synthesized today.
147
+ def self.synthesized_version(name)
148
+ case name
149
+ when "SocketRocket"
150
+ unless defined?(Helpers::Constants) && Helpers::Constants.respond_to?(:socket_rocket_config)
151
+ raise "[RNDepsFacades] Cannot synthesize facade for '#{name}': " \
152
+ "Helpers::Constants.socket_rocket_config is unavailable."
153
+ end
154
+ version = Helpers::Constants.socket_rocket_config[:version]
155
+ if version.nil? || version.to_s.strip.empty?
156
+ raise "[RNDepsFacades] Cannot synthesize facade for '#{name}': " \
157
+ "socket_rocket_config[:version] is missing or empty."
158
+ end
159
+ version.to_s
160
+ else
161
+ raise "[RNDepsFacades] No synthesized version rule for facaded pod '#{name}'. " \
162
+ "Add one to synthesized_version or give it a real podspec in FACADE_PODS."
163
+ end
164
+ end
165
+ private_class_method :synthesized_version
166
+
167
+ # Loads the real podspec so we can mirror its structure. A facaded pod with a
168
+ # declared podspec path MUST have a readable real podspec — if it's missing or
169
+ # unparseable we raise rather than ship an empty facade (which would silently
170
+ # drop subspecs / the version, the very drift this mechanism prevents).
171
+ def self.load_real_spec(path, name)
172
+ unless File.exist?(path)
173
+ raise "[RNDepsFacades] Real podspec for facaded pod '#{name}' not found at #{path}. " \
174
+ "Update FACADE_PODS in rndeps_facades.rb if the podspec moved."
175
+ end
176
+ begin
177
+ Pod::Specification.from_file(path)
178
+ rescue => e
179
+ raise "[RNDepsFacades] Failed to read real podspec for facaded pod '#{name}' at #{path}: #{e.message}"
180
+ end
181
+ end
182
+ private_class_method :load_real_spec
183
+
184
+ # Library (non-test, non-app) subspec names of the real spec, so third-party
185
+ # libs depending on `<pod>/<subspec>` (e.g. `RCT-Folly/Fabric`) keep
186
+ # resolving. Derived, never hand-listed.
187
+ def self.derive_subspecs(real)
188
+ real.subspecs
189
+ .reject { |ss| ss.test_specification? || (ss.respond_to?(:app_specification?) && ss.app_specification?) }
190
+ .map(&:base_name)
191
+ end
192
+ private_class_method :derive_subspecs
193
+ end
@@ -19,28 +19,46 @@ class SPMManager
19
19
  log 'Cleaning old SPM dependencies from Pods project'
20
20
  clean_spm_dependencies_from_target(project, @dependencies_by_pod)
21
21
  log 'Adding SPM dependencies to Pods project'
22
+ flattened_pod_names = []
22
23
  @dependencies_by_pod.each do |pod_name, dependencies|
24
+ target = project.targets.find { |t| t.name == pod_name}
23
25
  dependencies.each do |spm_spec|
24
26
  log "Adding SPM dependency on product #{spm_spec[:products]}"
25
27
  add_spm_to_target(
26
28
  project,
27
- project.targets.find { |t| t.name == pod_name},
29
+ target,
28
30
  spm_spec[:url],
29
31
  spm_spec[:requirement],
30
32
  spm_spec[:products]
31
33
  )
32
- log " Adding workaround for Swift package not found issue"
33
- target = project.targets.find { |t| t.name == pod_name}
34
- target.build_configurations.each do |config|
35
- target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
36
- search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
37
- unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
38
- target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
34
+ if target.product_type == 'com.apple.product-type.library.static'
35
+ # Xcode emits a package C-target's module map both into the shared
36
+ # products dir and into this pod's CONFIGURATION_BUILD_DIR, and both
37
+ # copies are unavoidably visible (the package swiftmodule serializes
38
+ # the products-root path) — clang hard-errors with "redefinition of
39
+ # module" on Xcode 26.3. Building the pod straight into the products
40
+ # root makes both paths the same file, and puts the package
41
+ # swiftmodule on the pod's default search path.
42
+ log " Building #{pod_name} into the shared products dir to avoid duplicate module maps"
43
+ target.build_configurations.each do |config|
44
+ target.build_settings(config.name)['CONFIGURATION_BUILD_DIR'] = '${PODS_CONFIGURATION_BUILD_DIR}'
45
+ end
46
+ flattened_pod_names << pod_name unless flattened_pod_names.include?(pod_name)
47
+ else
48
+ log " Adding workaround for Swift package not found issue"
49
+ target.build_configurations.each do |config|
50
+ target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
51
+ search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
52
+ unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
53
+ target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
54
+ end
39
55
  end
40
56
  end
41
57
  end
42
58
  end
43
59
 
60
+ rewrite_aggregate_modulemap_references(installer, flattened_pod_names) unless flattened_pod_names.empty?
61
+
44
62
  unless @dependencies_by_pod.empty?
45
63
  log_warning "If you're using Xcode 15 or earlier you might need to close and reopen the Xcode workspace"
46
64
  unless ENV["USE_FRAMEWORKS"] == "dynamic"
@@ -53,6 +71,55 @@ class SPMManager
53
71
 
54
72
  private
55
73
 
74
+ # Flattening a pod's build dir moves its generated modulemap from
75
+ # "<Pod>/<Pod>.modulemap" to "<Pod>.modulemap"; the aggregate xcconfigs
76
+ # reference the old path via -fmodule-map-file. Must mutate the in-memory
77
+ # Config attributes — later post_install steps (NewArchitectureHelper) re-save
78
+ # the same Config objects and would clobber a plain file edit.
79
+ def rewrite_aggregate_modulemap_references(installer, pod_names)
80
+ installer.aggregate_targets.each do |aggregate_target|
81
+ aggregate_target.xcconfigs.each do |config_name, config_file|
82
+ %w[OTHER_CFLAGS OTHER_SWIFT_FLAGS].each do |key|
83
+ value = config_file.attributes[key]
84
+ next unless value
85
+
86
+ updated_value = pod_names.reduce(value) do |acc, pod_name|
87
+ acc.gsub(
88
+ "${PODS_CONFIGURATION_BUILD_DIR}/#{pod_name}/#{pod_name}.modulemap",
89
+ "${PODS_CONFIGURATION_BUILD_DIR}/#{pod_name}.modulemap"
90
+ )
91
+ end
92
+ config_file.attributes[key] = updated_value if updated_value != value
93
+ end
94
+
95
+ config_file.save_as(aggregate_target.xcconfig_path(config_name))
96
+ end
97
+ end
98
+ end
99
+
100
+ # Creates a new object in the project with a UUID guaranteed not to collide
101
+ # with any UUID already present in the project.
102
+ #
103
+ # `Pod::Project` overrides `generate_available_uuid_list` with a fast,
104
+ # counter-based scheme (`<sha prefix><counter>0`) that deliberately skips
105
+ # collision checks, on the assumption that the whole Pods project is generated
106
+ # in a single pass. That assumption does not hold here: we run in a
107
+ # `post_install` hook, and the generator's counter can be out of sync with the
108
+ # UUIDs already assigned to existing objects (e.g. when the project has been
109
+ # reloaded from disk during an incremental install, the counter restarts at 0
110
+ # while the root object still occupies `<prefix>00000000`). Using `project.new`
111
+ # directly can therefore hand back a UUID that is already in use and overwrite
112
+ # an existing object (notably the root `PBXProject`), producing a Pods project
113
+ # Xcode refuses to load. We keep the deterministic scheme but probe forward
114
+ # until we find a UUID that is actually free.
115
+ def new_object(project, klass)
116
+ uuid = project.generate_uuid
117
+ uuid = project.generate_uuid while project.objects_by_uuid.key?(uuid)
118
+ object = klass.new(project, uuid)
119
+ object.initialize_defaults
120
+ object
121
+ end
122
+
56
123
  def log(msg)
57
124
  ::Pod::UI.puts "[SPM] #{msg}"
58
125
  end
@@ -76,7 +143,7 @@ class SPMManager
76
143
  pkg_class = Xcodeproj::Project::Object::XCLocalSwiftPackageReference
77
144
  pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.relative_path == url }
78
145
  if !pkg
79
- pkg = project.new(pkg_class)
146
+ pkg = new_object(project, pkg_class)
80
147
  pkg.relative_path = url
81
148
  log(" Adding local package to workspace: #{pkg.inspect}")
82
149
  project.root_object.package_references << pkg
@@ -85,7 +152,7 @@ class SPMManager
85
152
  pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
86
153
  pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url }
87
154
  if !pkg
88
- pkg = project.new(pkg_class)
155
+ pkg = new_object(project, pkg_class)
89
156
  pkg.repositoryURL = url
90
157
  pkg.requirement = requirement
91
158
  log(" Adding remote package to workspace: #{pkg.inspect}")
@@ -101,7 +168,7 @@ class SPMManager
101
168
  next if ref
102
169
 
103
170
  log(" Adding product dependency #{product_name} to #{target.name}")
104
- ref = project.new(ref_class)
171
+ ref = new_object(project, ref_class)
105
172
  ref.package = pkg
106
173
  ref.product_name = product_name
107
174
  target.package_product_dependencies << ref
@@ -0,0 +1,97 @@
1
+ // swift-tools-version: 6.0
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ // AUTO-GENERATED by scripts/setup-apple-spm.js – do not edit manually.
10
+ // This SPM-specific template replaces the CocoaPods codegen template
11
+ // with xcframework configuration built in.
12
+
13
+ import PackageDescription
14
+
15
+ // React headers need NO search paths: the React module comes from the
16
+ // invariant ReactHeaders Clang target,
17
+ // every other RN namespace from the ReactNativeHeaders binaryTarget
18
+ // (pure-RN), the third-party deps namespaces (folly/glog/boost/...) from the
19
+ // ReactNativeDependenciesHeaders sidecar, and the per-app generated headers
20
+ // from the ReactAppHeaders target below — all auto-served by SPM through
21
+ // product/target dependencies.
22
+ let headersDep: [Target.Dependency] = [
23
+ .product(name: "ReactHeaders", package: "ReactNative"),
24
+ .product(name: "ReactNativeHeaders", package: "ReactNative"),
25
+ .product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
26
+ "ReactAppHeaders",
27
+ ]
28
+
29
+ let package = Package(
30
+ name: "React-GeneratedCode",
31
+ platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)],
32
+ products: [
33
+ .library(
34
+ name: "ReactCodegen",
35
+ targets: ["ReactCodegen"]),
36
+ .library(
37
+ name: "ReactAppDependencyProvider",
38
+ targets: ["ReactAppDependencyProvider"]),
39
+ .library(
40
+ name: "ReactAppHeaders",
41
+ targets: ["ReactAppHeaders"]),
42
+ ],
43
+ dependencies: [
44
+ .package(name: "ReactNative", path: "../../xcframeworks"),
45
+ ],
46
+ targets: [
47
+ .target(
48
+ name: "ReactCodegen",
49
+ dependencies: headersDep,
50
+ path: "ReactCodegen",
51
+ exclude: ["ReactCodegen.podspec"],
52
+ publicHeadersPath: ".",
53
+ cSettings: [.headerSearchPath("headers")],
54
+ // DEBUG/NDEBUG match the prebuilt React.framework's config-gated C++
55
+ // ABI: NDEBUG (Release) strips DebugStringConvertible's vtable and
56
+ // shifts the ShadowNode layout that codegen'd Props inherit, so this
57
+ // target must compile with the same NDEBUG state or the Release link
58
+ // fails (undefined "vtable for DebugStringConvertible"). Mirrors
59
+ // packages/react-native/Package.swift.
60
+ cxxSettings: [
61
+ .headerSearchPath("headers"),
62
+ .define("DEBUG", .when(configuration: .debug)),
63
+ .define("NDEBUG", .when(configuration: .release)),
64
+ ],
65
+ linkerSettings: [
66
+ .linkedFramework("Foundation")
67
+ ]
68
+ ),
69
+ .target(
70
+ name: "ReactAppDependencyProvider",
71
+ dependencies: ["ReactCodegen"] + headersDep,
72
+ path: "ReactAppDependencyProvider",
73
+ exclude: ["ReactAppDependencyProvider.podspec"],
74
+ publicHeadersPath: ".",
75
+ cSettings: [.headerSearchPath(".."), .headerSearchPath("headers")],
76
+ cxxSettings: [
77
+ .headerSearchPath(".."),
78
+ .headerSearchPath("headers"),
79
+ // Match the prebuilt React.framework NDEBUG-gated C++ ABI (see above).
80
+ .define("DEBUG", .when(configuration: .debug)),
81
+ .define("NDEBUG", .when(configuration: .release)),
82
+ ],
83
+ linkerSettings: [
84
+ .linkedFramework("Foundation")
85
+ ]
86
+ ),
87
+ // The per-app generated-headers farm (built by spm sync), vended as a
88
+ // normal SPM headers target.
89
+ .target(
90
+ name: "ReactAppHeaders",
91
+ path: "ReactAppHeaders",
92
+ publicHeadersPath: "."
93
+ ),
94
+ ],
95
+ // React Native headers require C++20 (concepts, std::optional, etc.)
96
+ cxxLanguageStandard: .cxx20
97
+ )
@@ -80,6 +80,26 @@ source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
80
80
  HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine"
81
81
  [ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc"
82
82
 
83
+ # SwiftPM consumers have no Pods directory to provide hermesc. When there is
84
+ # no Pods installation at all and the current HERMES_CLI_PATH does not exist
85
+ # (unset, or an injector-baked path gone stale after a package-store move),
86
+ # resolve the hermes-compiler npm package THROUGH react-native's own dependency
87
+ # graph — react-native pins the hermes-compiler version, so the compiler's
88
+ # bytecode version always matches the prebuilt hermes VM artifacts (a
89
+ # mismatched pair crashes at launch with "Wrong bytecode version").
90
+ # STRICTLY SwiftPM-scoped: in a CocoaPods build the hermes-engine pod owns both
91
+ # the VM and hermesc, and its hermes train may legitimately differ from the npm
92
+ # hermes-compiler — falling back there would compile bundles the podded VM
93
+ # rejects.
94
+ if [[ ( -z "$PODS_ROOT" || ! -d "$PODS_ROOT" ) && ! -f "$HERMES_CLI_PATH" ]]; then
95
+ NODE_HERMESC=$(RN_DIR="$REACT_NATIVE_DIR" "$NODE_BINARY" --print \
96
+ "require('path').join(require('path').dirname(require.resolve('hermes-compiler/package.json', {paths: [process.env.RN_DIR]})), 'hermesc', 'osx-bin', 'hermesc')" \
97
+ 2>/dev/null || true)
98
+ if [[ -n "$NODE_HERMESC" && -f "$NODE_HERMESC" ]]; then
99
+ HERMES_CLI_PATH="$NODE_HERMESC"
100
+ fi
101
+ fi
102
+
83
103
  # If hermesc is not available and USE_HERMES is not set to false, show error.
84
104
  if [[ $USE_HERMES != false && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then
85
105
  echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \