first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

82
node_modules/react-native/scripts/bundle.js generated vendored Normal file
View File

@@ -0,0 +1,82 @@
/**
* 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';
const {bundleCommand: bc} = require('@react-native/community-cli-plugin');
const {execSync} = require('child_process');
const commander = require('commander');
const {readFileSync} = require('fs');
const path = require('path');
// Commander 12.0.0 changes from the global to named export
// $FlowFixMe[signature-verification-failure]
const program = commander.program ?? commander;
program.version(
JSON.parse(
readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8'),
).version,
);
program
.name(bc.name)
.description(bc.description ?? '')
.option(
'--config-cmd <string>',
'Command to generate a JSON project config',
'npx react-native config',
)
.option('--load-config <string>', 'JSON project config')
.option('--verbose', 'Additional logs', () => true, false)
.allowUnknownOption()
.action(async function handleAction() {
let config = null;
let options = program
.opts /*::<{
configCmd?: string,
loadConfig?: string,
verbose: boolean,
...
}>*/
();
if (options.loadConfig != null) {
config = JSON.parse(
options.loadConfig.replace(/^\W*'/, '').replace(/'\W*$/, ''),
);
} else if (options.configCmd != null) {
config = JSON.parse(
execSync(options.configCmd.trim(), {encoding: 'utf8'}),
);
}
if (config == null) {
throw new Error('No config provided');
}
await bc.func(program.args, config, options);
});
if (bc.options != null) {
for (const o of bc.options) {
program.option(
o.name,
o.description ?? '',
o.parse ?? (value => value),
o.default,
);
}
}
if (require.main === module) {
program.parse(process.argv);
}
module.exports = program;

View File

@@ -0,0 +1,212 @@
# 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.
require 'json'
require 'pathname'
require 'cocoapods'
require_relative './autolinking_utils.rb'
require_relative '../react_native_pods.rb'
# Your project will have to depend on the @react-native-community/cli if you use this method
# for listing React native modules.
#
# Parameters:
# - config_command: the command to run to get the application's current config, e.g. ['npx', '@react-native-community/cli', 'config']
def list_native_modules!(config_command)
if !(config_command.is_a? Array and config_command.size > 0)
Pod::UI.warn "Expected a list_native_modules! to be called with a config command", [
"Unable to autolink if no config is provided for the current project."
]
exit(1)
end
# Ignore stderr output, we're only interested in stdout and the return code. Libraries can output warnings to
# stderr which create problems for JSON deserializing.
json, _, status = Pod::Executable.capture_command(config_command[0], config_command[1..], capture: :both)
if not status.success?
Pod::UI.warn "The command: '#{config_command.join(" ").bold.yellow}' returned a status code of #{status.exitstatus.to_s.bold.red}", [
"In order to autolink using Cocoapods, this framework uses @react-native-community/cli to discover React Native native modules",
"Please either add it: yarn add -D @react-native-community/cli or consult your framework's documentation."
]
exit(status.exitstatus)
end
config = JSON.parse(json)
packages = config["dependencies"]
ios_project_root = Pathname.new(config["project"]["ios"]["sourceDir"])
react_native_path = Pathname.new(config["reactNativePath"])
codegen_output_path = ios_project_root.join("build/generated/autolinking/autolinking.json")
# Write autolinking react-native-config output to codegen folder
FileUtils.mkdir_p(File.dirname(codegen_output_path))
File.write(codegen_output_path, json)
found_pods = []
packages.each do |package_name, package|
next unless package_config = package["platforms"]["ios"]
name = package["name"]
podspec_path = package_config["podspecPath"]
script_phases = package_config["scriptPhases"]
configurations = package_config["configurations"]
# Add a warning to the queue and continue to the next dependency if the podspec_path is nil/empty
if podspec_path.nil? || podspec_path.empty?
Pod::UI.warn("list_native_modules! skipped the react-native dependency '#{name}'. No podspec file was found.",
[
"Check to see if there is an updated version that contains the necessary podspec file",
"Contact the library maintainers or send them a PR to add a podspec. The react-native-webview podspec is a good example of a package.json driven podspec. See https://github.com/react-native-community/react-native-webview/blob/master/react-native-webview.podspec",
"If necessary, you can disable autolinking for the dependency and link it manually. See https://github.com/react-native-community/cli/blob/main/docs/autolinking.md#how-can-i-disable-autolinking-for-unsupported-library"
])
next
end
spec = Pod::Specification.from_file(podspec_path)
# Skip pods that do not support the platform of the current target.
next unless AutolinkingUtils.is_platform_supported?(current_target_definition, spec)
podspec_dir_path = Pathname.new(File.dirname(podspec_path))
relative_path = podspec_dir_path.relative_path_from ios_project_root
found_pods.push({
"configurations": configurations,
"name": name,
"root": package["root"],
"path": relative_path.to_path,
"podspec_path": podspec_path,
"script_phases": script_phases
})
end
if found_pods.size > 0
pods = found_pods.map { |p| p[:name] }.sort.to_sentence
Pod::UI.puts "Found #{found_pods.size} #{"module".pluralize(found_pods.size)} for target `#{current_target_definition.name}`"
end
return {
"ios_packages": found_pods,
"ios_project_root_path": ios_project_root.to_s,
"react_native_path": react_native_path.relative_path_from(ios_project_root).to_s
}
end
# Your project will have to depend on the @react-native-community/cli if you use this method
# for listing React native modules.
#
# Parameters:
# - config:
# - :ios_packages - Array of React Native iOS packages, e.g. [{ package_name: "Foo", package: { .. }}, ...]
# - :ios_project_root_path - Absolute path to the react_native project's ios folder, e.g. /Users/foobar/project/rn_project/ios
# - :react_native_path - Relative path to the react_native from the project, e.g. ./node_modules/react-native
def link_native_modules!(config)
Pod::UI.puts "link_native_modules! #{config}"
if !(
config[:ios_packages].is_a? Array and
config[:ios_project_root_path].is_a? String and
config[:react_native_path].is_a? String
)
Pod::UI.warn("link_native_modules! has been called with a malformed 'config' parameter",
[
"This is the config argument passed: #{config.inspect}",
]);
exit(1)
end
ios_project_root = config[:ios_project_root_path]
packages = config[:ios_packages]
found_pods = []
packages.each do |package|
podspec_path = package[:podspec_path]
configurations = package[:configurations]
# Add a warning to the queue and continue to the next dependency if the podspec_path is nil/empty
if podspec_path.nil? || podspec_path.empty?
Pod::UI.warn("use_native_modules! skipped the react-native dependency '#{package[:name]}'. No podspec file was found.",
[
"Check to see if there is an updated version that contains the necessary podspec file",
"Contact the library maintainers or send them a PR to add a podspec. The react-native-webview podspec is a good example of a package.json driven podspec. See https://github.com/react-native-community/react-native-webview/blob/master/react-native-webview.podspec",
"If necessary, you can disable autolinking for the dependency and link it manually. See https://github.com/react-native-community/cli/blob/main/docs/autolinking.md#how-can-i-disable-autolinking-for-unsupported-library"
])
next
end
spec = Pod::Specification.from_file(podspec_path)
# Don't try track packages that exclude our platforms
next unless AutolinkingUtils.is_platform_supported?(current_target_definition, spec)
# We want to do a look up inside the current CocoaPods target
# to see if it's already included, this:
# 1. Gives you the chance to define it beforehand
# 2. Ensures CocoaPods won't explode if it's included twice
#
this_target = current_target_definition
existing_deps = current_target_definition.dependencies
# Skip dependencies that the user already activated themselves.
next if existing_deps.find do |existing_dep|
existing_dep.name.split('/').first == spec.name
end
podspec_dir_path = Pathname.new(File.dirname(podspec_path))
relative_path = podspec_dir_path.relative_path_from ios_project_root
# Register the found React Native module into our collection of Pods.
pod spec.name, :path => relative_path.to_path, :configurations => configurations
if package[:script_phases] && !this_target.abstract?
# Can be either an object, or an array of objects
Array(package[:script_phases]).each do |phase|
# see https://www.rubydoc.info/gems/cocoapods-core/Pod/Podfile/DSL#script_phase-instance_method
# for the full object keys
Pod::UI.puts "Adding a custom script phase for Pod #{spec.name}: #{phase["name"] || 'No name specified.'}"
# Support passing in a path relative to the root of the package
if phase["path"]
phase["script"] = File.read(File.expand_path(phase["path"], package[:root]))
phase.delete("path")
end
# Support converting the execution position into a symbol
phase["execution_position"] = phase["execution_position"]&.to_sym
phase = Hash[phase.map { |k, v| [k.to_sym, v] }]
script_phase phase
end
end
found_pods.push spec
end
if found_pods.size > 0
pods = found_pods.map { |p| p.name }.sort.to_sentence
Pod::UI.puts "Auto-linking React Native #{"module".pluralize(found_pods.size)} for target `#{current_target_definition.name}`: #{pods}"
end
return {
:reactNativePath => config[:react_native_path]
}
end
$default_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
# Autolink your native modules
#
# Parameters:
# - config_command: the command to run to get the application's current config, e.g. ['npx', '@react-native-community/cli', 'config'],
# you can override this if you'd like to avoid the dependency. e.g. ['cat', 'your_config.json']
def use_native_modules!(config_command = $default_command)
return link_native_modules!(list_native_modules!(config_command))
end

View File

@@ -0,0 +1,25 @@
# 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.
class AutolinkingUtils
def self.nested_path_exists?(object, *path)
path.reduce(object) do |obj, method|
return false unless obj.respond_to?(method)
obj.public_send(method)
end
return true
end
def self.is_platform_supported?(current_target_definition, spec)
platform = current_target_definition.platform
if !platform
# Historically we've supported platforms that aren't specifically excluded.
return true
end
return spec.supported_on_platform?(platform.name)
end
end

48
node_modules/react-native/scripts/cocoapods/codegen.rb generated vendored Normal file
View File

@@ -0,0 +1,48 @@
# 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.
require_relative './helpers.rb'
# keeping the run_codegen! method for testing purposes
def run_codegen!(
app_path,
config_file_dir,
new_arch_enabled: true,
disable_codegen: false,
react_native_path: "../node_modules/react-native",
fabric_enabled: false,
hermes_enabled: true,
codegen_output_dir: 'build/generated/ios',
config_key: 'codegenConfig',
package_json_file: '~/app/package.json',
folly_version: Helpers::Constants.folly_config()[:version],
codegen_utils: CodegenUtils.new()
)
if ENV["RCT_SKIP_CODEGEN"] == "1"
return
end
codegen_utils.use_react_native_codegen_discovery!(
disable_codegen,
app_path,
:react_native_path => react_native_path,
:fabric_enabled => fabric_enabled,
:hermes_enabled => hermes_enabled,
:config_file_dir => config_file_dir,
:codegen_output_dir => codegen_output_dir,
:config_key => config_key,
:folly_version => folly_version
)
end
def basePath(react_native_path, relative_installation_root)
expanded_path = File.expand_path(react_native_path)
if expanded_path == react_native_path
react_native_path
else
File.join(relative_installation_root.to_s, react_native_path)
end
end

View File

@@ -0,0 +1,13 @@
# 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.
class CodegenScriptPhaseExtractor
def initialize()
end
def extract_script_phase(options)
get_script_phases_with_codegen_discovery(options)
end
end

View File

@@ -0,0 +1,126 @@
# 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.
require 'json'
require_relative './utils.rb'
require_relative './helpers.rb'
require_relative './codegen_script_phase_extractor.rb'
class CodegenUtils
def initialize()
end
@@REACT_CODEGEN_DISCOVERY_DONE = false
def self.set_react_codegen_discovery_done(value)
@@REACT_CODEGEN_DISCOVERY_DONE = value
end
def self.react_codegen_discovery_done
@@REACT_CODEGEN_DISCOVERY_DONE
end
def use_react_native_codegen_discovery!(
codegen_disabled,
app_path,
react_native_path: "../node_modules/react-native",
fabric_enabled: false,
hermes_enabled: true,
config_file_dir: '',
codegen_output_dir: 'build/generated/ios',
config_key: 'codegenConfig',
folly_version: Helpers::Constants.folly_config[:version],
codegen_utils: CodegenUtils.new(),
file_manager: File,
logger: CodegenUtils::UI
)
return if codegen_disabled
if CodegenUtils.react_codegen_discovery_done()
logger.puts("Skipping use_react_native_codegen_discovery.")
return
end
if !app_path
logger.warn("Error: app_path is required for use_react_native_codegen_discovery.")
logger.warn("If you are calling use_react_native_codegen_discovery! in your Podfile, please remove the call and pass `app_path` and/or `config_file_dir` to `use_react_native!`.")
abort
end
relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
out = Pod::Executable.execute_command(
'node',
[
"#{relative_installation_root}/#{react_native_path}/scripts/generate-codegen-artifacts.js",
"-p", "#{app_path}",
"-o", Pod::Config.instance.installation_root,
"-t", "ios",
])
Pod::UI.puts out;
CodegenUtils.set_react_codegen_discovery_done(true)
end
@@CLEANUP_DONE = false
def self.set_cleanup_done(newValue)
@@CLEANUP_DONE = newValue
end
def self.cleanup_done
return @@CLEANUP_DONE
end
def self.clean_up_build_folder(rn_path, codegen_dir, dir_manager: Dir, file_manager: File)
if ENV["RCT_SKIP_CODEGEN"] == "1"
return
end
return if CodegenUtils.cleanup_done()
CodegenUtils.set_cleanup_done(true)
ios_folder = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
codegen_path = file_manager.join(ios_folder, codegen_dir)
return if !dir_manager.exist?(codegen_path)
FileUtils.rm_rf("#{codegen_path}")
base_provider_path = file_manager.join(rn_path, 'React', 'Fabric', 'RCTThirdPartyFabricComponentsProvider')
FileUtils.rm_rf("#{base_provider_path}.h")
FileUtils.rm_rf("#{base_provider_path}.mm")
CodegenUtils.assert_codegen_folder_is_empty(codegen_path, dir_manager: dir_manager)
end
# Need to split this function from the previous one to be able to test it properly.
def self.assert_codegen_folder_is_empty(codegen_path, dir_manager: Dir)
# double check that the files have actually been deleted.
# Emit an error message if not.
if dir_manager.exist?(codegen_path) && dir_manager.glob("#{codegen_path}/*").length() != 0
Pod::UI.warn "Unable to remove the content of #{codegen_path} folder. Please run rm -rf #{codegen_path} and try again."
abort
end
end
class UI
# ANSI escape codes for colors and formatting
CYAN = "\e[36m"
YELLOW = "\e[33m"
BOLD = "\e[1m"
RESET = "\e[0m"
class << self
def puts(text, info: false)
prefix = "#{CYAN}#{BOLD}[Codegen]#{RESET}"
message = info ? "#{YELLOW}#{text}#{RESET}" : text
Pod::UI.puts "#{prefix} #{message}"
end
def warn(text)
puts(text, info: true)
end
end
end
end

17
node_modules/react-native/scripts/cocoapods/fabric.rb generated vendored Normal file
View File

@@ -0,0 +1,17 @@
# 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.
# It sets up the Fabric dependencies.
#
# @parameter react_native_path: relative path to react-native
def setup_fabric!(react_native_path: "../node_modules/react-native")
pod 'React-Fabric', :path => "#{react_native_path}/ReactCommon"
pod 'React-FabricComponents', :path => "#{react_native_path}/ReactCommon"
pod 'React-graphics', :path => "#{react_native_path}/ReactCommon/react/renderer/graphics"
pod 'React-RCTFabric', :path => "#{react_native_path}/React", :modular_headers => true
pod 'React-ImageManager', :path => "#{react_native_path}/ReactCommon/react/renderer/imagemanager/platform/ios"
pod 'React-FabricImage', :path => "#{react_native_path}/ReactCommon"
end

151
node_modules/react-native/scripts/cocoapods/helpers.rb generated vendored Normal file
View File

@@ -0,0 +1,151 @@
# 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.
# Helper object to wrap the invocation of sysctl
# This makes it easier to mock the behaviour in tests
class SysctlChecker
def call_sysctl_arm64
return `/usr/sbin/sysctl -n hw.optional.arm64 2>&1`.to_i
end
end
# Helper class that is used to easily send commands to Xcodebuild
# And that can be subclassed for testing purposes.
class Xcodebuild
def self.version
`xcodebuild -version`
end
end
# Helper object to wrap system properties like RUBY_PLATFORM
# This makes it easier to mock the behaviour in tests
class Environment
def ruby_platform
return RUBY_PLATFORM
end
end
class Finder
def self.find_codegen_file(path)
js_files = '-name "Native*.js" -or -name "*NativeComponent.js"'
ts_files = '-name "Native*.ts" -or -name "*NativeComponent.ts"'
return `find #{path} -type f \\( #{js_files} -or #{ts_files} \\)`.split("\n").sort()
end
end
module Helpers
class Constants
@@boost_config = {
:git => "https://github.com/react-native-community/boost-for-react-native",
:compiler_flags => '-Wno-documentation'
}
@@socket_rocket_config = {
:version => '0.7.1'
}
@@folly_config = {
:version => '2024.11.18.00',
:git => 'https://github.com/facebook/folly.git',
:compiler_flags => '-DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_CFG_NO_COROUTINES=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -Wno-comma -Wno-shorten-64-to-32',
:config_file => [
"#pragma once",
"",
"#define FOLLY_MOBILE 1",
"#define FOLLY_USE_LIBCPP 1",
"#define FOLLY_HAVE_PTHREAD 1",
"#define FOLLY_CFG_NO_COROUTINES 1",
"#define FOLLY_HAVE_CLOCK_GETTIME 1",
"",
'#pragma clang diagnostic ignored "-Wcomma"',
],
:dep_name => 'RCT-Folly/Fabric'
}
@@fast_float_config = {
:git => "https://github.com/fastfloat/fast_float.git",
}
@@fmt_config = {
:git => "https://github.com/fmtlib/fmt.git",
}
@@glog_config = {
:git => "https://github.com/google/glog.git",
}
@@double_conversion_config = {
:git => "https://github.com/google/double-conversion.git",
}
def self.min_ios_version_supported
return '15.1'
end
def self.min_xcode_version_supported
return '16.1'
end
def self.folly_config
return @@folly_config
end
def self.set_folly_config(new_folly_config)
@@folly_config.update(new_folly_config)
end
def self.boost_config
return @@boost_config
end
def self.set_boost_config(new_boost_config)
@@boost_config.update(new_boost_config)
end
def self.socket_rocket_config
return @@socket_rocket_config
end
def self.set_socket_rocket_config(new_socket_rocket_config)
@@socket_rocket_config.update(new_socket_rocket_config)
end
def self.fast_float_config
return @@fast_float_config
end
def self.set_fast_float_config(new_fast_float_config)
@@fast_float_config.update(new_fast_float_config)
end
def self.fmt_config
return @@fmt_config
end
def self.set_fmt_config(new_fmt_config)
@@fmt_config.update(new_fmt_config)
end
def self.glog_config
return @@glog_config
end
def self.set_glog_config(new_glog_config)
@@glog_config.update(new_glog_config)
end
def self.double_conversion_config
return @@double_conversion_config
end
def self.set_double_conversion_config(new_double_conversion_config)
@@double_conversion_config.update(new_double_conversion_config)
end
def self.cxx_language_standard
return "c++20"
end
end
end

View File

@@ -0,0 +1,56 @@
# 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.
require_relative './utils.rb'
# It sets up the Hermes.
#
# @parameter react_native_path: relative path to react-native
# @parameter fabric_enabled: whether Fabirc is enabled
def setup_hermes!(react_native_path: "../node_modules/react-native")
react_native_dir = Pod::Config.instance.installation_root.join(react_native_path)
# This `:tag => hermestag` below is only to tell CocoaPods to update hermes-engine when React Native version changes.
# We have custom logic to compute the source for hermes-engine. See sdks/hermes-engine/*
hermestag_file_name = ENV['RCT_HERMES_V1_ENABLED'] == "1" ? ".hermesv1version" : ".hermesversion"
hermestag_file = File.join(react_native_dir, "sdks", hermestag_file_name)
hermestag = File.exist?(hermestag_file) ? File.read(hermestag_file).strip : ''
pod 'hermes-engine', :podspec => "#{react_native_path}/sdks/hermes-engine/hermes-engine.podspec", :tag => hermestag
pod 'React-hermes', :path => "#{react_native_path}/ReactCommon/hermes"
end
def use_third_party_jsc
return ENV['USE_THIRD_PARTY_JSC'] == '1'
end
# use Hermes is the default. The only other option is the third-party JSC
# if the 3rd party JSC is not true, we always want to use Hermes.
def use_hermes
return !use_third_party_jsc()
end
def use_hermes_flags
return "-DUSE_HERMES=1"
end
def use_third_party_jsc_flags
return "-DUSE_THIRD_PARTY_JSC=1"
end
def js_engine_flags()
if use_hermes()
return use_hermes_flags()
else
return use_third_party_jsc_flags()
end
end
# Utility function to depend on JS engine based on the environment variable.
def depend_on_js_engine(s)
if use_hermes()
s.dependency 'hermes-engine'
elsif use_third_party_jsc()
s.dependency 'React-jsc'
end
end

View File

@@ -0,0 +1,51 @@
# 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.
# Monkeypatch of `Pod::Lockfile` to ensure automatic update of dependencies integrated with a local podspec when their version changed.
# This is necessary because local podspec dependencies must be otherwise manually updated.
module LocalPodspecPatch
# Returns local podspecs whose versions differ from the one in the `react-native` package.
def self.pods_to_update(react_native_path: "../node_modules/react-native", dir_manager: Dir, file_manager: File)
@@local_podspecs = dir_manager.glob("#{react_native_path}/third-party-podspecs/*").map { |file| file_manager.basename(file, ".podspec") }
@@local_podspecs = @@local_podspecs.select do |podspec_name|
# Read local podspec to determine the cached version
local_podspec_path = file_manager.join(
dir_manager.pwd, "Pods/Local Podspecs/#{podspec_name}.podspec.json"
)
# Local podspec cannot be outdated if it does not exist, yet
next unless file_manager.exist?(local_podspec_path)
local_podspec = file_manager.read(local_podspec_path)
local_podspec_json = JSON.parse(local_podspec)
local_version = local_podspec_json["version"]
# Read the version from a podspec from the `react-native` package
podspec_path = "#{react_native_path}/third-party-podspecs/#{podspec_name}.podspec"
current_podspec = Pod::Specification.from_file(podspec_path)
current_version = current_podspec.version.to_s
current_version != local_version
end
@@local_podspecs
end
# Patched `detect_changes_with_podfile` method
def detect_changes_with_podfile(podfile)
Pod::UI.puts "Invoke detect_changes_with_podfile patched method".red
changes = super(podfile)
return patch_detect_changes_with_podfile(changes)
end
def patch_detect_changes_with_podfile(changes)
@@local_podspecs.each do |local_podspec|
next unless changes[:unchanged].include?(local_podspec)
changes[:unchanged].delete(local_podspec)
changes[:changed] << local_podspec
end
changes
end
end

View File

@@ -0,0 +1,199 @@
# 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.
require 'json'
require_relative "./utils.rb"
require_relative "./helpers.rb"
require_relative "./jsengine.rb"
class NewArchitectureHelper
@@NewArchWarningEmitted = false # Used not to spam warnings to the user.
def self.set_clang_cxx_language_standard_if_needed(installer)
cxxBuildsettingsName = "CLANG_CXX_LANGUAGE_STANDARD"
projects = installer.aggregate_targets
.map{ |t| t.user_project }
.uniq{ |p| p.path }
projects.each do |project|
Pod::UI.puts("Setting #{cxxBuildsettingsName} to #{ Helpers::Constants::cxx_language_standard } on #{ project.path }")
project.build_configurations.each do |config|
config.build_settings[cxxBuildsettingsName] = Helpers::Constants::cxx_language_standard
end
project.save()
end
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
target_installation_result.native_target.build_configurations.each do |config|
config.build_settings[cxxBuildsettingsName] = Helpers::Constants::cxx_language_standard
end
end
# Override targets that would set spec.xcconfig to define c++ version
installer.aggregate_targets.each do |aggregate_target|
aggregate_target.xcconfigs.each do |config_name, config_file|
config_file.attributes[cxxBuildsettingsName] = Helpers::Constants::cxx_language_standard
end
end
end
def self.computeFlags(is_new_arch_enabled)
new_arch_flag = is_new_arch_enabled ? "-DRCT_NEW_ARCH_ENABLED=1 " : ""
return " #{new_arch_flag}"
end
def self.modify_flags_for_new_architecture(installer, is_new_arch_enabled)
# Add flags to Target pods xcconfig
installer.aggregate_targets.each do |aggregate_target|
aggregate_target.xcconfigs.each do |config_name, config_file|
ReactNativePodsUtils.add_flag_to_map_with_inheritance(config_file.attributes, "OTHER_CPLUSPLUSFLAGS", self.computeFlags(is_new_arch_enabled))
xcconfig_path = aggregate_target.xcconfig_path(config_name)
config_file.save_as(xcconfig_path)
end
end
# Add flags to Target pods xcconfig
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
# The React-Core pod may have a suffix added by Cocoapods, so we test whether 'React-Core' is a substring, and do not require exact match
if pod_name.include? 'React-Core'
target_installation_result.native_target.build_configurations.each do |config|
ReactNativePodsUtils.add_flag_to_map_with_inheritance(config.build_settings, "OTHER_CPLUSPLUSFLAGS", self.computeFlags(is_new_arch_enabled))
end
end
end
end
def self.install_modules_dependencies(spec, new_arch_enabled, folly_version = Helpers::Constants.folly_config[:version])
# Pod::Specification does not have getters so, we have to read
# the existing values from a hash representation of the object.
hash = spec.to_hash
compiler_flags = hash["compiler_flags"] ? hash["compiler_flags"] : ""
current_config = hash["pod_target_xcconfig"] != nil ? hash["pod_target_xcconfig"] : {}
current_headers = current_config["HEADER_SEARCH_PATHS"] != nil ? current_config["HEADER_SEARCH_PATHS"] : ""
header_search_paths = ["\"$(PODS_ROOT)/Headers/Private/Yoga\""]
if ENV['USE_FRAMEWORKS']
ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-graphics", "React_graphics", ["react/renderer/graphics/platform/ios"])
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Fabric", "React_Fabric", ["react/renderer/components/view/platform/cxx"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-FabricImage", "React_FabricImage", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCommon", "ReactCommon", ["react/nativemodule/core"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-runtimeexecutor", "React_runtimeexecutor", ["platform/ios"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-NativeModulesApple", "React_NativeModulesApple", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-RCTFabric", "RCTFabric", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-utils", "React_utils", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-featureflags", "React_featureflags", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-debug", "React_debug", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-ImageManager", "React_ImageManager", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-rendererdebug", "React_rendererdebug", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-renderercss", "React_renderercss", []))
.each { |search_path|
header_search_paths << "\"#{search_path}\""
}
end
header_search_paths_string = header_search_paths.join(" ")
spec.compiler_flags = compiler_flags.empty? ? self.computeFlags(new_arch_enabled).strip! : "#{compiler_flags} #{self.computeFlags(new_arch_enabled)}"
current_config["HEADER_SEARCH_PATHS"] = current_headers.empty? ?
header_search_paths_string :
"#{current_headers} #{header_search_paths_string}"
current_config["CLANG_CXX_LANGUAGE_STANDARD"] = Helpers::Constants::cxx_language_standard
spec.dependency "React-Core"
ReactNativePodsUtils.add_flag_to_map_with_inheritance(current_config, "OTHER_CPLUSPLUSFLAGS", self.computeFlags(new_arch_enabled))
ReactNativePodsUtils.add_flag_to_map_with_inheritance(current_config, "OTHER_SWIFT_FLAGS", new_arch_enabled ? " -DRCT_NEW_ARCH_ENABLED" : "")
spec.dependency "React-RCTFabric" # This is for Fabric Component
spec.dependency "ReactCodegen"
spec.dependency "RCTRequired"
spec.dependency "RCTTypeSafety"
spec.dependency "ReactCommon/turbomodule/bridging"
spec.dependency "ReactCommon/turbomodule/core"
spec.dependency "React-NativeModulesApple"
spec.dependency "Yoga"
spec.dependency "React-Fabric"
spec.dependency "React-graphics"
spec.dependency "React-utils"
spec.dependency "React-featureflags"
spec.dependency "React-debug"
spec.dependency "React-ImageManager"
spec.dependency "React-rendererdebug"
spec.dependency 'React-jsi'
spec.dependency 'React-renderercss'
depend_on_js_engine(spec)
add_rn_third_party_dependencies(spec)
add_rncore_dependency(spec)
spec.pod_target_xcconfig = current_config
end
def self.extract_react_native_version(react_native_path, file_manager: File, json_parser: JSON)
package_json_file = File.join(react_native_path, "package.json")
if !file_manager.exist?(package_json_file)
raise "Couldn't find the React Native package.json file at #{package_json_file}"
end
package = json_parser.parse(file_manager.read(package_json_file))
return package["version"]
end
# Deprecated method. This has been restored because some libraries (e.g. react-native-exit-app) still use it.
def self.folly_compiler_flags
folly_config = Helpers::Constants.folly_config
return folly_config[:compiler_flags]
end
def self.new_arch_enabled
return true
end
def self.set_RCTNewArchEnabled_in_info_plist(installer, new_arch_enabled)
projectPaths = installer.aggregate_targets
.map{ |t| t.user_project }
.uniq{ |p| p.path }
.map{ |p| p.path }
excluded_info_plist = ["/Pods", "Tests", "metainternal", ".bundle", "build/", "DerivedData/", ".xcframework", ".framework", "watchkitapp", "today-extention"]
projectPaths.each do |projectPath|
projectFolderPath = File.dirname(projectPath)
infoPlistFiles = `find #{projectFolderPath} -name "Info.plist"`
infoPlistFiles = infoPlistFiles.split("\n").map { |f| f.strip }
infoPlistFiles.each do |infoPlistFile|
# If infoPlistFile contains Pods or tests, skip it
should_skip = false
excluded_info_plist.each do |excluded|
if infoPlistFile.include? excluded
should_skip = true
end
end
next if should_skip
# Read the file as a plist
begin
info_plist = Xcodeproj::Plist.read_from_path(infoPlistFile)
rescue StandardError => e
Pod::UI.warn("Failed to read Info.plist at #{infoPlistFile}: #{e.message}")
next
end
# Check if it contains the RCTNewArchEnabled key
if info_plist["RCTNewArchEnabled"] and info_plist["RCTNewArchEnabled"] == new_arch_enabled
next
end
# Add the key and value to the plist
info_plist["RCTNewArchEnabled"] = new_arch_enabled ? true : false
Xcodeproj::Plist.write_to_path(info_plist, infoPlistFile)
end
end
end
end

View File

@@ -0,0 +1,194 @@
# 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.
module PrivacyManifestUtils
def self.add_aggregated_privacy_manifest(installer)
user_project = get_user_project_from(installer)
targets = get_application_targets(user_project)
file_path = get_privacyinfo_file_path(user_project, targets)
privacy_info = read_privacyinfo_file(file_path) || {
"NSPrivacyCollectedDataTypes" => [],
"NSPrivacyTracking" => false
}
# Get all required reason APIs defined in current pods
required_reason_apis = get_used_required_reason_apis(installer)
# Add the Required Reason APIs from React Native core
get_core_accessed_apis.each do |accessed_api|
api_type = accessed_api["NSPrivacyAccessedAPIType"]
reasons = accessed_api["NSPrivacyAccessedAPITypeReasons"]
required_reason_apis[api_type] ||= []
required_reason_apis[api_type] += reasons
end
# Merge the Required Reason APIs from pods with the ones from the existing PrivacyInfo file
(privacy_info["NSPrivacyAccessedAPITypes"] || []).each do |accessed_api|
api_type = accessed_api["NSPrivacyAccessedAPIType"]
reasons = accessed_api["NSPrivacyAccessedAPITypeReasons"]
# Add reasons from existing PrivacyInfo file to the ones from pods
required_reason_apis[api_type] ||= []
required_reason_apis[api_type] += reasons
end
# Update the existing PrivacyInfo file with the new aggregated data
privacy_info["NSPrivacyAccessedAPITypes"] = required_reason_apis.map { |api_type, reasons|
{
"NSPrivacyAccessedAPIType" => api_type,
"NSPrivacyAccessedAPITypeReasons" => reasons.uniq
}
}
Xcodeproj::Plist.write_to_path(privacy_info, file_path)
targets.each do |target|
ensure_reference(file_path, user_project, target)
end
end
def self.get_application_targets(user_project)
return user_project.targets.filter { |t| t.respond_to?(:symbol_type) && t.symbol_type == :application }
end
def self.read_privacyinfo_file(file_path)
# Maybe add missing default NSPrivacyTracking, NSPrivacyTrackingDomains, NSPrivacyCollectedDataTypes, but this works without those keys
source_data = nil
# Try to read an existing PrivacyInfo.xcprivacy file
begin
source_data = Xcodeproj::Plist.read_from_path(file_path)
Pod::UI.puts "[Privacy Manifest Aggregation] Appending aggregated reasons to existing PrivacyInfo.xcprivacy file."
rescue => e
Pod::UI.puts "[Privacy Manifest Aggregation] No existing PrivacyInfo.xcprivacy file found, creating a new one."
end
return source_data
end
def self.ensure_reference(file_path, user_project, target)
privacy_info_filename = File.basename(file_path)
# Check if the file is already in the PBXBuildFile section
build_phase_reference_exists = target.resources_build_phase.files_references.any? { |file_ref| file_ref&.path&.end_with?(privacy_info_filename) }
unless build_phase_reference_exists
# Check if the file is already in the PBXFileReference section
existing_file_reference = user_project.files.find { |file| file.path&.end_with?(privacy_info_filename) }
if existing_file_reference
# If the file reference exists, add it to the build phase
target.resources_build_phase.add_file_reference(existing_file_reference, true)
else
# If the file reference doesn't exist, add it to the project and the build phase
# We try to find the main group, but if it doesn't exist, we default to adding the file to the project root - both work
file_root = user_project.root_object.main_group.children.find { |group|
group.class == Xcodeproj::Project::Object::PBXGroup && (group.name == target.name || group.path == target.name)
} || user_project
puts "file_root: #{file_root}"
file_ref = file_root.new_file(file_path)
target.resources_build_phase.add_file_reference(file_ref, true)
end
end
end
def self.get_privacyinfo_file_path(user_project, targets)
file_refs = targets.flat_map { |target| target.resources_build_phase.files_references }
existing_file = file_refs.find { |file_ref| file_ref&.path&.end_with?("PrivacyInfo.xcprivacy") }
if existing_file
return existing_file.real_path
end
# We try to find a file we know exists in the project to get the path to the main group directory
info_plist_path = user_project.files.find { |file_ref| file_ref.name == "Info.plist" }
if info_plist_path.nil?
# return path that is sibling to .xcodeproj
path = user_project.path
return File.join(File.dirname(path), "PrivacyInfo.xcprivacy")
end
return File.join(File.dirname(info_plist_path.real_path),"PrivacyInfo.xcprivacy")
end
def self.get_used_required_reason_apis(installer)
# A dictionary with keys of type string (NSPrivacyAccessedAPIType) and values of type string[] (NSPrivacyAccessedAPITypeReasons[])
used_apis = {}
Pod::UI.puts "[Privacy Manifest Aggregation] Reading .xcprivacy files to aggregate all used Required Reason APIs."
installer.pod_targets.each do |pod_target|
# puts pod_target
pod_target.file_accessors.each do |file_accessor|
file_accessor.resource_bundles.each do |bundle_name, bundle_files|
bundle_files.each do |file_path|
# This needs to be named like that due to apple requirements
if File.basename(file_path) == 'PrivacyInfo.xcprivacy'
content = Xcodeproj::Plist.read_from_path(file_path)
accessed_api_types = content["NSPrivacyAccessedAPITypes"]
accessed_api_types&.each do |accessed_api|
api_type = accessed_api["NSPrivacyAccessedAPIType"]
reasons = accessed_api["NSPrivacyAccessedAPITypeReasons"]
next unless api_type
used_apis[api_type] ||= []
used_apis[api_type] += reasons
end
end
end
end
end
end
return used_apis
end
def self.get_privacy_manifest_paths_from(user_project)
privacy_manifests = user_project
.files
.select { |p|
p&.path&.end_with?('PrivacyInfo.xcprivacy')
}
return privacy_manifests
end
def self.get_core_accessed_apis()
file_timestamp_accessed_api = {
"NSPrivacyAccessedAPIType" => "NSPrivacyAccessedAPICategoryFileTimestamp",
"NSPrivacyAccessedAPITypeReasons" => ["C617.1"],
}
user_defaults_accessed_api = {
"NSPrivacyAccessedAPIType" => "NSPrivacyAccessedAPICategoryUserDefaults",
"NSPrivacyAccessedAPITypeReasons" => ["CA92.1"],
}
boot_time_accessed_api = {
"NSPrivacyAccessedAPIType" => "NSPrivacyAccessedAPICategorySystemBootTime",
"NSPrivacyAccessedAPITypeReasons" => ["35F9.1"],
}
return [file_timestamp_accessed_api, user_defaults_accessed_api, boot_time_accessed_api]
end
def self.get_user_project_from(installer)
user_project = installer.aggregate_targets
.map{ |t| t.user_project }
.first
return user_project
end
def self.add_privacy_manifest_if_needed(installer)
user_project = get_user_project_from(installer)
privacy_manifest = self.get_privacy_manifest_paths_from(user_project).first
if privacy_manifest.nil?
privacy_manifest = {
"NSPrivacyCollectedDataTypes" => [],
"NSPrivacyTracking" => false,
"NSPrivacyAccessedAPITypes" => get_core_accessed_apis
}
path = File.join(user_project&.path.parent, "PrivacyInfo.xcprivacy")
Xcodeproj::Plist.write_to_path(privacy_manifest, path)
Pod::UI.puts "Your app does not have a privacy manifest! A template has been generated containing Required Reasons API usage in the core React Native library. Please add the PrivacyInfo.xcprivacy file to your project and complete data use, tracking and any additional required reasons your app is using according to Apple's guidance: https://developer.apple.com/documentation/bundleresources/privacy_manifest_files. Then, you will need to manually add this file to your project in Xcode.".red
end
end
end

453
node_modules/react-native/scripts/cocoapods/rncore.rb generated vendored Normal file
View File

@@ -0,0 +1,453 @@
# 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.
require 'json'
require 'net/http'
require 'rexml/document'
require_relative './utils.rb'
### Adds ReactNativeCore-prebuilt as a dependency to the given podspec if we're not
### building ReactNativeCore from source (then this function does nothing).
def add_rncore_dependency(s)
if !ReactNativeCoreUtils.build_rncore_from_source()
current_pod_target_xcconfig = s.to_hash["pod_target_xcconfig"] || {}
current_pod_target_xcconfig = current_pod_target_xcconfig.to_h unless current_pod_target_xcconfig.is_a?(Hash)
s.dependency "React-Core-prebuilt"
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] ||= [] << "$(PODS_ROOT)/React-Core-prebuilt/React.xcframework/Headers"
s.pod_target_xcconfig = current_pod_target_xcconfig
end
end
## - RCT_USE_PREBUILT_RNCORE: If set to 1, it will use the release tarball from Maven instead of building from source.
## - RCT_TESTONLY_RNCORE_TARBALL_PATH: **TEST ONLY** If set, it will use a local tarball of RNCore if it exists.
## - RCT_TESTONLY_RNCORE_VERSION: **TEST ONLY** If set, it will override the version of RNCore to be used.
## - RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS: If set to 1, it will download the dSYMs for the prebuilt RNCore frameworks and install these in the framework folders
class ReactNativeCoreUtils
@@build_from_source = true
@@react_native_path = ""
@@react_native_version = ""
@@use_nightly = false
@@download_dsyms = false
## Sets up wether ReactNative Core should be built from source or not.
## If RCT_USE_PREBUILT_RNCORE is set to 1 and the artifacts exists on Maven, it will
## not build from source. Otherwise, it will build from source.
def self.setup_rncore(react_native_path, react_native_version)
# We don't want setup to be called multiple times, so we check if the variables are already set.
if @@react_native_version == ""
rncore_log("Setting up ReactNativeCore...")
@@react_native_path = react_native_path
@@react_native_version = ENV["RCT_TESTONLY_RNCORE_VERSION"] == nil ? react_native_version : ENV["RCT_TESTONLY_RNCORE_VERSION"]
@@download_dsyms = ENV["RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS"] == "1"
if @@react_native_version.include? "nightly"
@@use_nightly = true
if ENV["RCT_TESTONLY_RNCORE_VERSION"] == "nightly"
@@react_native_version = ReactNativeDependenciesUtils.get_nightly_npm_version()
rncore_log("Using nightly version from npm: #{@@react_native_version}")
else
rncore_log("Using nightly build #{@@react_native_version}")
end
end
if ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]
abort_if_use_local_rncore_with_no_file()
end
use_local_xcframework = ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"] && File.exist?(ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"])
artifacts_exists = ENV["RCT_USE_PREBUILT_RNCORE"] == "1" && (@@use_nightly ? nightly_artifact_exists(@@react_native_version) : release_artifact_exists(@@react_native_version))
@@build_from_source = !use_local_xcframework && !artifacts_exists
if @@build_from_source && ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"] && !use_local_xcframework
rncore_log("No local xcframework found, reverting to building from source.")
end
if @@build_from_source && ENV["RCT_USE_PREBUILT_RNCORE"] && !artifacts_exists
rncore_log("No prebuilt artifacts found, reverting to building from source.")
end
rncore_log("Building from source: #{@@build_from_source}")
end
end
def self.abort_if_use_local_rncore_with_no_file()
if !File.exist?(ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"])
abort("RCT_TESTONLY_RNCORE_TARBALL_PATH is set to #{ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]} but the file does not exist!")
end
end
def self.build_rncore_from_source()
return @@build_from_source
end
def self.resolve_podspec_source()
if ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]
abort_if_use_local_rncore_with_no_file()
rncore_log("Using local xcframework at #{ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]}")
return {:http => "file://#{ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]}" }
end
if ENV["RCT_USE_PREBUILT_RNCORE"] == "1"
if @@use_nightly
begin
return self.podspec_source_download_prebuilt_nightly_tarball()
rescue => e
rncore_log("Failed to download nightly tarball: #{e.message}", :error)
return
end
end
begin
return self.podspec_source_download_prebuild_stable_tarball()
rescue => e
rncore_log("Failed to download release tarball: #{e.message}", :error)
return
end
end
end
def self.podspec_source_download_prebuild_stable_tarball()
if @@react_native_path == ""
rncore_log("react_native_path is not set", :error)
return
end
if @@react_native_version == ""
rncore_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
destinationDebug = download_stable_rncore(@@react_native_path, @@react_native_version, :debug)
destinationRelease = download_stable_rncore(@@react_native_path, @@react_native_version, :release)
if @@download_dsyms
dSymsDebug = download_stable_rncore(@@react_native_path, @@react_native_version, :debug, true)
dSymsRelease = download_stable_rncore(@@react_native_path, @@react_native_version, :release, true)
rncore_log("Resolved stable dSYMs")
rncore_log(" #{Pathname.new(dSymsDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(dSymsRelease).relative_path_from(Pathname.pwd).to_s}")
# Make sure that the dSYMs are processed
process_dsyms(destinationDebug, dSymsDebug)
process_dsyms(destinationRelease, dSymsRelease)
end
rncore_log("Resolved stable ReactNativeCore-prebuilt version:")
rncore_log(" #{Pathname.new(destinationDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(destinationRelease).relative_path_from(Pathname.pwd).to_s}")
return {:http => URI::File.build(path: destinationDebug).to_s }
end
def self.podspec_source_download_prebuilt_nightly_tarball()
if @@react_native_path == ""
rncore_log("react_native_path is not set", :error)
return
end
if @@react_native_version == ""
rncore_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
destinationDebug = download_nightly_rncore(@@react_native_path, @@react_native_version, :debug)
destinationRelease = download_nightly_rncore(@@react_native_path, @@react_native_version, :release)
if @@download_dsyms
dSymsDebug = download_nightly_rncore(@@react_native_path, @@react_native_version, :debug, true)
dSymsRelease = download_nightly_rncore(@@react_native_path, @@react_native_version, :release, true)
rncore_log("Resolved nightly dSYMs")
rncore_log(" #{Pathname.new(dSymsDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(dSymsRelease).relative_path_from(Pathname.pwd).to_s}")
# Make sure that the dSYMs are processed
process_dsyms(destinationDebug, dSymsDebug)
process_dsyms(destinationRelease, dSymsRelease)
end
rncore_log("Resolved nightly ReactNativeCore-prebuilt version:")
rncore_log(" #{Pathname.new(destinationDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(destinationRelease).relative_path_from(Pathname.pwd).to_s}")
return {:http => URI::File.build(path: destinationDebug).to_s }
end
def self.process_dsyms(frameworkTarball, dSymsTarball)
if !@@download_dsyms
return
end
if @@react_native_path == ""
rncore_log("react_native_path is not set", :error)
return
end
if @@react_native_version == ""
rncore_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
# gunzip the dSymsTarball and the frameworkTarball into a temporary folder
# and then copy the dSYMs into the framework folder and then tar/gz the framework folder again
# into the same location as the original frameworkTarball
rncore_log("Adding symbols #{Pathname.new(dSymsTarball).relative_path_from(Pathname.pwd).to_s} to framework tarball #{Pathname.new(frameworkTarball).relative_path_from(Pathname.pwd).to_s}")
FileUtils.mkdir_p(File.dirname(frameworkTarball))
FileUtils.cp(frameworkTarball, "#{frameworkTarball}.orig")
rncore_log(" Backed up original tarballs")
begin
# Now let's gunzip the framework tarball into a .tar file
# Get filename and foldername from the tarball path
frameworkFolder = File.dirname(frameworkTarball)
frameworkFilename = File.basename(frameworkTarball, ".tar.gz")
frameworkTarPath = File.join(frameworkFolder, frameworkFilename + ".tar")
# Now gunzip the tarball into the frameworkFolder - this will remove the .gz file and leave us with a .tar file
rncore_log(" Unpacking framework tarball")
`gunzip "#{frameworkTarball}"`
# Now let's untar the dSyms tarball into a temporary folder / dSYMs subfolder
dsyms_tmp_dir = "#{artifacts_dir}/dSYMs"
rncore_log(" Unpacking dSYMs to temporary folder")
`mkdir -p "#{dsyms_tmp_dir}" && tar -xzf "#{dSymsTarball}" -C "#{dsyms_tmp_dir}"`
# Now we need to remap the symbol files to be relative to the framework folder
remap_sourcemaps_for_symbols(dsyms_tmp_dir)
# Add the dSYMs folder to the framework folder
rncore_log(" Adding dSYMs to framework tarball")
# Move symbol bundles into each of the slices in the xcframework
# Example:
# move dSYMs/ios-arm64/. into React.xcframework/ios-arm64/React.framework/dSYMs/.
Dir.glob(File.join(dsyms_tmp_dir, "*")).each do |dsym_path|
slice_name = File.basename(dsym_path)
slice_dsym_dest = File.join("React.xcframework", slice_name, "React.framework", "dSYMs")
rncore_log(" Adding dSYM slice #{slice_name} into tarball at #{slice_dsym_dest}")
`(cd "#{File.dirname(frameworkTarPath)}" && mkdir -p "#{slice_dsym_dest}" && cp -R "#{dsym_path}/." "#{slice_dsym_dest}" && tar -rf "#{frameworkTarPath}" "#{slice_dsym_dest}")`
end
# Now gzip the framework tarball again - remember to use the .tar file and not the .gz file
rncore_log(" Packing #{Pathname.new(frameworkTarPath).relative_path_from(Pathname.pwd).to_s}")
`gzip -1 "#{frameworkTarPath}"`
# Clean up the temporary folder
FileUtils.remove_entry(dsyms_tmp_dir)
rncore_log(" Processed dSYMs into framework tarball #{Pathname.new(frameworkTarball).relative_path_from(Pathname.pwd).to_s}")
# Remove backup of original tarballs
FileUtils.rm_f("#{frameworkTarball}.orig")
# Remove temp dSYMs folder and the temp Framework folder
FileUtils.rm_rf(dsyms_tmp_dir)
FileUtils.rm_rf(File.join(artifacts_dir, "React.xcframework"))
rescue => e
rncore_log("Failed to process dSYMs: #{e.message}", :error)
# Restore the original tarballs
FileUtils.mv("#{frameworkTarball}.orig", frameworkTarball) if File.exist?("#{frameworkTarball}.orig")
rncore_log("Restored original tarballs", :error)
abort "Couldn't process dSYMs: #{e.message}"
end
end
def self.remap_sourcemaps_for_symbols(symbolsPath)
rncore_log(" Remapping dSYMs to be relative to framework folder")
# Find all .dSYM bundles in the symbols path
dsym_bundles = []
Dir.glob(File.join(symbolsPath, "**", "*.dSYM")).each do |path|
if File.directory?(path)
# Check if it's a valid dSYM bundle with Info.plist
info_plist = File.join(path, 'Contents', 'Info.plist')
dsym_bundles << path if File.exist?(info_plist)
end
end
return if dsym_bundles.empty?
# Define source path mappings - from absolute build paths to relative framework paths
# Expand the path relative to the installation root (project root, parent of ios/)
react_native_absolute_path = File.expand_path(@@react_native_path, Pod::Config.instance.installation_root)
mappings = [
["/Users/runner/work/react-native/react-native/packages/react-native", react_native_absolute_path],
]
dsym_bundles.each do |dsym_path| begin
# Get UUIDs for this dSYM bundle
uuid_output = `dwarfdump --uuid "#{dsym_path}" 2>/dev/null`
uuids = uuid_output.scan(/UUID:\s+([0-9A-F-]{36})/i).flatten
next if uuids.empty?
# Create Resources directory if it doesn't exist
resources_dir = File.join(dsym_path, 'Contents', 'Resources')
FileUtils.mkdir_p(resources_dir)
# Generate plist content with path mappings
plist_content = generate_plist_content(mappings)
# Write plist for each UUID
uuids.each do |uuid|
plist_path = File.join(resources_dir, "#{uuid}.plist")
File.write(plist_path, plist_content)
end
rescue => e
rncore_log(" Failed to process dSYM #{dsym_path}: #{e.message}", :error)
end
end
rncore_log(" Completed dSYM remapping for #{dsym_bundles.length} bundles")
end
def self.generate_plist_content(mappings)
# Generate the source path remapping entries
remapping_entries = mappings.map do |from, to|
" <key>#{from}</key><string>#{to}</string>"
end.join("\n")
# Use the first mapping for legacy keys
first_from, first_to = mappings.first
return <<~PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DBGVersion</key><string>3</string>
<key>DBGBuildSourcePath</key><string>#{first_from}</string>
<key>DBGSourcePath</key><string>#{first_to}</string>
<key>DBGSourcePathRemapping</key>
<dict>
#{remapping_entries}
</dict>
</dict>
</plist>
PLIST
end
def self.stable_tarball_url(version, build_type, dsyms = false)
## You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded.
## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central.
maven_repo_url =
ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ?
ENV['ENTERPRISE_REPOSITORY'] :
"https://repo1.maven.org/maven2"
group = "com/facebook/react"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.0/react-native-artifacts-0.81.0-reactnative-core-debug.tar.gz
return "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-core-#{dsyms ? "dSYM-" : ""}#{build_type.to_s}.tar.gz"
end
def self.nightly_tarball_url(version, configuration, dsyms = false)
artefact_coordinate = "react-native-artifacts"
artefact_name = "reactnative-core-#{dsyms ? "dSYM-" : ""}#{configuration ? configuration : "debug"}.tar.gz"
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artefact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
response = Net::HTTP.get_response(URI(xml_url))
if response.is_a?(Net::HTTPSuccess)
xml = REXML::Document.new(response.body)
timestamp = xml.elements['metadata/versioning/snapshot/timestamp'].text
build_number = xml.elements['metadata/versioning/snapshot/buildNumber'].text
full_version = "#{version}-#{timestamp}-#{build_number}"
final_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artefact_coordinate}/#{version}-SNAPSHOT/#{artefact_coordinate}-#{full_version}-#{artefact_name}"
return final_url
else
return ""
end
end
def self.download_stable_rncore(react_native_path, version, configuration, dsyms = false)
tarball_url = stable_tarball_url(version, configuration, dsyms)
download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms)
end
def self.download_nightly_rncore(react_native_path, version, configuration, dsyms = false)
tarball_url = nightly_tarball_url(version, configuration, dsyms)
download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms)
end
def self.download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms = false)
destination_path = configuration == nil ?
"#{artifacts_dir()}/reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}.tar.gz" :
"#{artifacts_dir()}/reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}-#{configuration}.tar.gz"
unless File.exist?(destination_path)
# Download to a temporary file first so we don't cache incomplete downloads.
rncore_log("Downloading ReactNativeCore-prebuilt #{dsyms ? "dSYMs " : ""}#{configuration ? configuration.to_s : ""} tarball from #{tarball_url} to #{Pathname.new(destination_path).relative_path_from(Pathname.pwd).to_s}")
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
else
rncore_log("Using downloaded ReactNativeCore-prebuilt #{dsyms ? "dSYMs " : ""}#{configuration ? configuration.to_s : ""} tarball at #{Pathname.new(destination_path).relative_path_from(Pathname.pwd).to_s}")
end
return destination_path
end
def self.release_artifact_exists(version)
return artifact_exists(stable_tarball_url(version, :debug))
end
def self.nightly_artifact_exists(version)
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
end
def self.artifacts_dir()
return File.join(Pod::Config.instance.project_pods_root, "ReactNativeCore-artifacts")
end
# This function checks that ReactNativeCore artifact exists on the maven repo
def self.artifact_exists(tarball_url)
# -L is used to follow redirects, useful for the nightlies
# I also needed to wrap the url in quotes to avoid escaping & and ?.
return (`curl -o /dev/null --silent -Iw '%{http_code}' -L "#{tarball_url}"` == "200")
end
def self.rncore_log(message, level = :info)
if !Object.const_defined?("Pod::UI")
return
end
log_message = '[ReactNativeCore] '
case level
when :info
Pod::UI.puts log_message.green + message
when :error
Pod::UI.puts log_message.red + message
else
Pod::UI.puts log_message.yellow + message
end
end
def self.get_nightly_npm_version()
uri = URI('https://registry.npmjs.org/react-native/nightly')
response = Net::HTTP.get_response(uri)
unless response.is_a?(Net::HTTPSuccess)
raise "Couldn't get an answer from NPM: #{response.code} #{response.message}"
end
json = JSON.parse(response.body)
latest_nightly = json['version']
return latest_nightly
end
end

View File

@@ -0,0 +1,294 @@
# 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.
require "json"
require 'net/http'
require 'rexml/document'
require_relative './utils.rb'
## There are two environment variables that is related to ReactNativeDependencies:
## - RCT_USE_RN_DEP: If set to 1, it will use the release tarball from Maven instead of building from source.
## - RCT_USE_LOCAL_RN_DEP: **TEST ONLY** If set, it will use a local tarball of ReactNativeDependencies if it exists.
## - RCT_DEPS_VERSION: **TEST ONLY** If set, it will override the version of ReactNativeDependencies to be used.
### Adds ReactNativeDependencies as a dependency to the given podspec if we're not
### building ReactNativeDependencies from source.
def add_rn_third_party_dependencies(s)
current_pod_target_xcconfig = s.to_hash["pod_target_xcconfig"] || {}
current_pod_target_xcconfig = current_pod_target_xcconfig.to_h unless current_pod_target_xcconfig.is_a?(Hash)
if ReactNativeDependenciesUtils.build_react_native_deps_from_source()
s.dependency "glog"
s.dependency "boost"
s.dependency "DoubleConversion"
s.dependency "fast_float"
s.dependency "fmt"
s.dependency "RCT-Folly"
s.dependency "SocketRocket"
if ENV["RCT_NEW_ARCH_ENABLED"]
s.dependency "RCT-Folly/Fabric"
end
header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
if header_search_paths.is_a?(String)
header_search_paths = header_search_paths.split(" ")
end
header_search_paths << "$(PODS_ROOT)/glog"
header_search_paths << "$(PODS_ROOT)/boost"
header_search_paths << "$(PODS_ROOT)/DoubleConversion"
header_search_paths << "$(PODS_ROOT)/fast_float/include"
header_search_paths << "$(PODS_ROOT)/fmt/include"
header_search_paths << "$(PODS_ROOT)/SocketRocket"
header_search_paths << "$(PODS_ROOT)/RCT-Folly"
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths
else
s.dependency "ReactNativeDependencies"
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] ||= [] << "$(PODS_ROOT)/ReactNativeDependencies"
end
s.pod_target_xcconfig = current_pod_target_xcconfig
end
class ReactNativeDependenciesUtils
@@build_from_source = true
@@react_native_path = ""
@@react_native_version = ""
@@use_nightly = false
def self.build_react_native_deps_from_source()
return @@build_from_source
end
def self.resolve_podspec_source()
if ENV["RCT_USE_LOCAL_RN_DEP"]
abort_if_use_local_rndeps_with_no_file()
rndeps_log("Using local xcframework at #{ENV["RCT_USE_LOCAL_RN_DEP"]}")
return {:http => "file://#{ENV["RCT_USE_LOCAL_RN_DEP"]}" }
end
if ENV["RCT_USE_RN_DEP"] && ENV["RCT_USE_RN_DEP"] == "1"
if @@use_nightly
begin
return self.podspec_source_download_prebuilt_nightly_tarball(@@react_native_version)
rescue => e
rndeps_log("Failed to download nightly tarball: #{e.message}", :error)
return
end
end
begin
return self.podspec_source_download_prebuild_release_tarball()
rescue => e
rndeps_log("Failed to download release tarball: #{e.message}", :error)
return
end
end
end
## Sets up wether react-native-dependencies should be built from source or not.
## If RCT_USE_RN_DEP is set to 1 and the artifacts exists on Maven, it will
## not build from source. Otherwise, it will build from source.
def self.setup_react_native_dependencies(react_native_path, react_native_version)
# We don't want setup to be called multiple times, so we check if the variables are already set.
if @@react_native_version == ""
rndeps_log("Setting up ReactNativeDependencies...")
@@react_native_path = react_native_path
@@react_native_version = ENV["RCT_DEPS_VERSION"] == nil ? react_native_version : ENV["RCT_DEPS_VERSION"]
if @@react_native_version.include? 'nightly'
@@use_nightly = true
if ENV["RCT_DEPS_VERSION"] == "nightly"
@@react_native_version = ReactNativeDependenciesUtils.get_nightly_npm_version()
rndeps_log("Using nightly version from npm: #{@@react_native_version}")
else
rndeps_log("Using nightly build #{@@react_native_version}")
end
end
if ENV["RCT_USE_LOCAL_RN_DEP"]
abort_if_use_local_rndeps_with_no_file()
end
artifacts_exists = ENV["RCT_USE_RN_DEP"] == "1" && (@@use_nightly ? nightly_artifact_exists(@@react_native_version) : release_artifact_exists(@@react_native_version))
use_local_xcframework = ENV["RCT_USE_LOCAL_RN_DEP"] && File.exist?(ENV["RCT_USE_LOCAL_RN_DEP"])
@@build_from_source = !use_local_xcframework && !artifacts_exists
if @@build_from_source && ENV["RCT_USE_LOCAL_RN_DEP"] && !use_local_xcframework
rndeps_log("No local xcframework found, reverting to building from source.")
end
if @@build_from_source && ENV["RCT_USE_PREBUILT_RNCORE"] && !artifacts_exists
rndeps_log("No prebuilt artifacts found, reverting to building from source.")
end
rndeps_log("Building from source: #{@@build_from_source}")
rndeps_log("Source: #{self.resolve_podspec_source()}")
end
end
def self.abort_if_use_local_rndeps_with_no_file()
if !File.exist?(ENV["RCT_USE_LOCAL_RN_DEP"])
abort("RCT_USE_LOCAL_RN_DEP is set to #{ENV["RCT_USE_LOCAL_RN_DEP"]} but the file does not exist!")
end
end
def self.podspec_source_download_prebuild_release_tarball()
# Warn if @@react_native_path is not set
if @@react_native_path == ""
rndeps_log("react_native_path is not set", :error)
return
end
# Warn if @@react_native_version is not set
if @@react_native_version == ""
rndeps_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
url = release_tarball_url(@@react_native_version, :debug)
rndeps_log("Using tarball from URL: #{url}")
destinationDebug = download_stable_rndeps(@@react_native_path, @@react_native_version, :debug)
download_stable_rndeps(@@react_native_path, @@react_native_version, :release)
return {:http => URI::File.build(path: destinationDebug).to_s }
end
def self.release_tarball_url(version, build_type)
## You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded.
## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central.
maven_repo_url =
ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ?
ENV['ENTERPRISE_REPOSITORY'] :
"https://repo1.maven.org/maven2"
group = "com/facebook/react"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.79.0-rc.0/react-native-artifacts-0.79.0-rc.0-reactnative-dependencies-debug.tar.gz
return "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-dependencies-#{build_type.to_s}.tar.gz"
end
def self.nightly_tarball_url(version, build_type)
artifact_coordinate = "react-native-artifacts"
artifact_name = "reactnative-dependencies-#{build_type.to_s}.tar.gz"
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artifact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
response = Net::HTTP.get_response(URI(xml_url))
if response.is_a?(Net::HTTPSuccess)
xml = REXML::Document.new(response.body)
timestamp = xml.elements['metadata/versioning/snapshot/timestamp'].text
build_number = xml.elements['metadata/versioning/snapshot/buildNumber'].text
full_version = "#{version}-#{timestamp}-#{build_number}"
final_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artifact_coordinate}/#{version}-SNAPSHOT/#{artifact_coordinate}-#{full_version}-#{artifact_name}"
return final_url
else
return ""
end
end
def self.download_nightly_rndeps(react_native_path, version, configuration)
tarball_url = nightly_tarball_url(version, configuration)
download_rndeps_tarball(react_native_path, tarball_url, version, configuration)
end
def self.download_stable_rndeps(react_native_path, version, configuration)
tarball_url = release_tarball_url(version, configuration)
download_rndeps_tarball(react_native_path, tarball_url, version, configuration)
end
def self.podspec_source_download_prebuilt_nightly_tarball(version)
# Warn if @@react_native_path is not set
if @@react_native_path == ""
rndeps_log("react_native_path is not set", :error)
return
end
# Warn if @@react_native_version is not set
if @@react_native_version == ""
rndeps_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
url = nightly_tarball_url(version, :debug)
rndeps_log("Using tarball from URL: #{url}")
destinationDebug = download_nightly_rndeps(@@react_native_path, @@react_native_version, :debug)
download_nightly_rndeps(@@react_native_path, @@react_native_version, :release)
return {:http => URI::File.build(path: destinationDebug).to_s }
return {:http => url}
end
def self.download_rndeps_tarball(react_native_path, tarball_url, version, configuration)
destination_path = configuration == nil ?
"#{artifacts_dir()}/reactnative-dependencies-#{version}.tar.gz" :
"#{artifacts_dir()}/reactnative-dependencies-#{version}-#{configuration}.tar.gz"
unless File.exist?(destination_path)
# Download to a temporary file first so we don't cache incomplete downloads.
tmp_file = "#{artifacts_dir()}/reactnative-dependencies.download"
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
end
return destination_path
end
def self.release_artifact_exists(version)
return artifact_exists(release_tarball_url(version, :debug))
end
def self.nightly_artifact_exists(version)
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
end
def self.artifacts_dir()
return File.join(Pod::Config.instance.project_pods_root, "ReactNativeDependencies-artifacts")
end
# This function checks that ReactNativeDependencies artifact exists on the maven repo
def self.artifact_exists(tarball_url)
# -L is used to follow redirects, useful for the nightlies
# I also needed to wrap the url in quotes to avoid escaping & and ?.
return (`curl -o /dev/null --silent -Iw '%{http_code}' -L "#{tarball_url}"` == "200")
end
def self.rndeps_log(message, level = :info)
if !Object.const_defined?("Pod::UI")
return
end
case level
when :info
Pod::UI.puts '[ReactNativeDependencies] '.green + message
when :error
Pod::UI.puts '[ReactNativeDependencies] '.red + message
else
Pod::UI.puts '[ReactNativeDependencies] '.yellow + message
end
end
def self.get_nightly_npm_version()
uri = URI('https://registry.npmjs.org/react-native/nightly')
response = Net::HTTP.get_response(uri)
unless response.is_a?(Net::HTTPSuccess)
raise "Couldn't get an answer from NPM: #{response.code} #{response.message}"
end
json = JSON.parse(response.body)
latest_nightly = json['version']
return latest_nightly
end
end

18
node_modules/react-native/scripts/cocoapods/runtime.rb generated vendored Normal file
View File

@@ -0,0 +1,18 @@
# 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.
# Set up Bridgeless dependencies
#
# @parameter react_native_path: relative path to react-native
def setup_bridgeless!(react_native_path: "../node_modules/react-native", use_hermes: true)
pod "React-jsitracing", :path => "#{react_native_path}/ReactCommon/hermes/executor/"
pod "React-runtimescheduler", :path => "#{react_native_path}/ReactCommon/react/renderer/runtimescheduler"
pod 'React-RuntimeCore', :path => "#{react_native_path}/ReactCommon/react/runtime"
pod 'React-RuntimeApple', :path => "#{react_native_path}/ReactCommon/react/runtime/platform/ios"
if use_hermes
pod 'React-RuntimeHermes', :path => "#{react_native_path}/ReactCommon/react/runtime"
end
end

94
node_modules/react-native/scripts/cocoapods/spm.rb generated vendored Normal file
View File

@@ -0,0 +1,94 @@
# 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.
class SPMManager
def initialize()
@dependencies_by_pod = {}
end
def dependency(pod_spec, url:, requirement:, products:)
@dependencies_by_pod[pod_spec.name] ||= []
@dependencies_by_pod[pod_spec.name] << { url: url, requirement: requirement, products: products}
end
def apply_on_post_install(installer)
project = installer.pods_project
log 'Cleaning old SPM dependencies from Pods project'
clean_spm_dependencies_from_target(project, @dependencies_by_pod)
log 'Adding SPM dependencies to Pods project'
@dependencies_by_pod.each do |pod_name, dependencies|
dependencies.each do |spm_spec|
log "Adding SPM dependency on product #{spm_spec[:products]}"
add_spm_to_target(
project,
project.targets.find { |t| t.name == pod_name},
spm_spec[:url],
spm_spec[:requirement],
spm_spec[:products]
)
log " Adding workaround for Swift package not found issue"
target = project.targets.find { |t| t.name == pod_name}
target.build_configurations.each do |config|
target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
end
end
end
end
unless @dependencies_by_pod.empty?
log_warning "If you're using Xcode 15 or earlier you might need to close and reopen the Xcode workspace"
unless ENV["USE_FRAMEWORKS"] == "dynamic"
@dependencies_by_pod.each do |pod_name, dependencies|
log_warning "Pod #{pod_name} is using swift package(s) #{dependencies.map{|i| i[:products]}.flatten.uniq.join(", ")} with static linking, this might cause linker errors. Consider using USE_FRAMEWORKS=dynamic, see https://github.com/facebook/react-native/pull/44627#issuecomment-2123119711 for more information"
end
end
end
end
private
def log(msg)
::Pod::UI.puts "[SPM] #{msg}"
end
def log_warning(msg)
::Pod::UI.puts "\n\n[SPM] WARNING!!! #{msg}\n\n"
end
def clean_spm_dependencies_from_target(project, new_targets)
project.root_object.package_references.delete_if { |pkg| (pkg.class == Xcodeproj::Project::Object::XCRemoteSwiftPackageReference) }
end
def add_spm_to_target(project, target, url, requirement, products)
pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency
pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url }
if !pkg
pkg = project.new(pkg_class)
pkg.repositoryURL = url
pkg.requirement = requirement
log(" Adding package to workspace: #{pkg.inspect}")
project.root_object.package_references << pkg
end
products.each do |product_name|
ref = target.package_product_dependencies.find do |r|
r.class == ref_class && r.package == pkg && r.product_name == product_name
end
next if ref
log(" Adding product dependency #{product_name} to #{target.name}")
ref = project.new(ref_class)
ref.package = pkg
ref.product_name = product_name
target.package_product_dependencies << ref
end
end
end
SPM = SPMManager.new

727
node_modules/react-native/scripts/cocoapods/utils.rb generated vendored Normal file
View File

@@ -0,0 +1,727 @@
# 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.
require 'shellwords'
require_relative "./helpers.rb"
require_relative "./jsengine.rb"
# Utilities class for React Native Cocoapods
class ReactNativePodsUtils
def self.warn_if_not_on_arm64
if SysctlChecker.new().call_sysctl_arm64() == 1 && !Environment.new().ruby_platform().include?('arm64')
Pod::UI.warn 'Do not use "pod install" from inside Rosetta2 (x86_64 emulation on arm64).'
Pod::UI.warn ' - Emulated x86_64 is slower than native arm64'
Pod::UI.warn ' - May result in mixed architectures in rubygems (eg: ffi_c.bundle files may be x86_64 with an arm64 interpreter)'
Pod::UI.warn 'Run "env /usr/bin/arch -arm64 /bin/bash --login" then try again.'
end
end
# deprecated. These checks are duplicated in the react_native_pods function
# and we don't really need them. Removing this function will make it easy to
# move forward.
def self.get_default_flags
flags = {
:fabric_enabled => false,
:hermes_enabled => true,
}
if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
flags[:fabric_enabled] = true
flags[:hermes_enabled] = true
end
if !use_hermes()
flags[:hermes_enabled] = false
end
return flags
end
def self.has_pod(installer, name)
installer.pods_project.pod_group(name) != nil
end
def self.set_gcc_preprocessor_definition_for_React_hermes(installer)
if ENV['RCT_HERMES_V1_ENABLED'] == "1"
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1 HERMES_V1_ENABLED=1", "React-hermes", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1 HERMES_V1_ENABLED=1", "React-RuntimeHermes", :debug)
else
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-hermes", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-RuntimeHermes", :debug)
end
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "hermes-engine", :debug)
end
def self.set_gcc_preprocessor_definition_for_debugger(installer)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED=1", "React-jsinspector", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED=1", "React-jsinspectornetwork", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED=1", "React-RCTNetwork", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED=1", "React-networking", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1", "React-jsinspector", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1", "React-jsinspectornetwork", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1", "React-RCTNetwork", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "REACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1", "React-networking", :debug)
end
def self.turn_off_resource_bundle_react_core(installer)
# this is needed for Xcode 14, see more details here https://github.com/facebook/react-native/issues/34673
# we should be able to remove this once CocoaPods catches up to it, see more details here https://github.com/CocoaPods/CocoaPods/issues/11402
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
if pod_name.to_s == 'React-Core'
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
end
def self.set_build_setting(installer, build_setting:, value:, config_name: nil)
Pod::UI.puts("Setting #{build_setting} build settings")
projects = self.extract_projects(installer)
projects.each do |project|
project.build_configurations.each do |config|
if config_name == nil || config.name == config_name
config.build_settings[build_setting] = value
end
end
project.save()
end
end
def self.set_ccache_compiler_and_linker_build_settings(installer, react_native_path, ccache_enabled)
projects = self.extract_projects(installer)
ccache_path = `command -v ccache`.strip
ccache_available = !ccache_path.empty?
message_prefix = "[Ccache]"
if ccache_available
Pod::UI.puts("#{message_prefix}: Ccache found at #{ccache_path}")
end
# Using scripts wrapping the ccache executable, to allow injection of configurations
ccache_clang_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang.sh')
ccache_clangpp_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang++.sh')
if ccache_available and ccache_enabled
Pod::UI.puts("#{message_prefix}: Setting CC, LD, CXX & LDPLUSPLUS build settings")
projects.each do |project|
project.build_configurations.each do |config|
# Using the un-qualified names means you can swap in different implementations, for example ccache
config.build_settings["CC"] = ccache_clang_sh
config.build_settings["LD"] = ccache_clang_sh
config.build_settings["CXX"] = ccache_clangpp_sh
config.build_settings["LDPLUSPLUS"] = ccache_clangpp_sh
config.build_settings["CCACHE_BINARY"] = ccache_path
end
project.save()
end
elsif ccache_available and !ccache_enabled
Pod::UI.puts("#{message_prefix}: Pass ':ccache_enabled => true' to 'react_native_post_install' in your Podfile or set environment variable 'USE_CCACHE=1' to increase the speed of subsequent builds")
elsif !ccache_available and ccache_enabled
Pod::UI.warn("#{message_prefix}: Install ccache or ensure your neither passing ':ccache_enabled => true' nor setting environment variable 'USE_CCACHE=1'")
else
Pod::UI.puts("#{message_prefix}: Removing Ccache from CC, LD, CXX & LDPLUSPLUS build settings")
projects.each do |project|
project.build_configurations.each do |config|
# Using the un-qualified names means you can swap in different implementations, for example ccache
config.build_settings["CC"] = config.build_settings["CC"].gsub(/#{Regexp.escape(ccache_clang_sh)}/, '') if config.build_settings["CC"]
config.build_settings["LD"] = config.build_settings["LD"].gsub(/#{Regexp.escape(ccache_clang_sh)}/, "") if config.build_settings["LD"]
config.build_settings["CXX"] = config.build_settings["CXX"].gsub(/#{Regexp.escape(ccache_clangpp_sh)}/, "") if config.build_settings["CXX"]
config.build_settings["LDPLUSPLUS"] = config.build_settings["LDPLUSPLUS"].gsub(/#{Regexp.escape(ccache_clangpp_sh)}/, "") if config.build_settings["LDPLUSPLUS"]
end
project.save()
end
end
end
def self.fix_library_search_paths(installer)
projects = self.extract_projects(installer)
projects.each do |project|
project.build_configurations.each do |config|
self.fix_library_search_path(config)
end
project.native_targets.each do |target|
target.build_configurations.each do |config|
self.fix_library_search_path(config)
end
end
project.save()
end
end
def self.apply_mac_catalyst_patches(installer)
# Fix bundle signing issues
installer.pods_project.targets.each do |target|
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
target.build_configurations.each do |config|
config.build_settings['CODE_SIGN_IDENTITY[sdk=macosx*]'] = '-'
end
end
end
installer.aggregate_targets.each do |aggregate_target|
aggregate_target.user_project.native_targets.each do |target|
target.build_configurations.each do |config|
# Explicitly set dead code stripping flags
config.build_settings['DEAD_CODE_STRIPPING'] = 'YES'
config.build_settings['PRESERVE_DEAD_CODE_INITS_AND_TERMS'] = 'YES'
# Modify library search paths
config.build_settings['LIBRARY_SEARCH_PATHS'] = ['$(SDKROOT)/usr/lib/swift', '$(SDKROOT)/System/iOSSupport/usr/lib/swift', '$(inherited)']
end
end
aggregate_target.user_project.save()
end
end
private
def self.add_build_settings_to_pod(installer, settings_name, settings_value, target_pod_name, configuration_type)
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
if pod_name.to_s == target_pod_name
target_installation_result.native_target.build_configurations.each do |config|
if configuration_type == nil || (configuration_type != nil && config.type == configuration_type)
config.build_settings[settings_name] ||= '$(inherited) '
config.build_settings[settings_name] << settings_value
end
end
end
end
end
def self.fix_library_search_path(config)
lib_search_paths = config.build_settings["LIBRARY_SEARCH_PATHS"]
if lib_search_paths == nil
# No search paths defined, return immediately
return
end
if lib_search_paths.include?("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)") || lib_search_paths.include?("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"")
# $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) causes problem with Xcode 12.5 + arm64 (Apple Silicon)
# since the libraries there are only built for x86_64 and i386.
lib_search_paths.delete("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)")
lib_search_paths.delete("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"")
end
if !(lib_search_paths.include?("$(SDKROOT)/usr/lib/swift") || lib_search_paths.include?("\"$(SDKROOT)/usr/lib/swift\""))
# however, $(SDKROOT)/usr/lib/swift is required, at least if user is not running CocoaPods 1.11
lib_search_paths.insert(0, "$(SDKROOT)/usr/lib/swift")
end
end
def self.create_xcode_env_if_missing(file_manager: File)
relative_path = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
file_path = file_manager.join(relative_path, '.xcode.env')
if !file_manager.exist?(file_path)
system("echo 'export NODE_BINARY=$(command -v node)' > #{file_path}")
end
if !file_manager.exist?("#{file_path}.local")
# When installing pods with a yarn alias, yarn creates a fake yarn and node executables
# in a temporary folder.
# Using `node --print "process.argv[0]";` we are able to retrieve the actual path from which node is running.
# see https://github.com/facebook/react-native/issues/43285 for more info. We've tweaked this slightly.
node_binary = Shellwords.escape(`node --print "process.argv[0]"`.strip)
system("echo 'export NODE_BINARY=#{node_binary}' > #{file_path}.local")
end
end
# It examines the target_definition property and sets the appropriate value for
# ENV['USE_FRAMEWORKS'] variable.
#
# - parameter target_definition: The current target definition
def self.detect_use_frameworks(target_definition)
if ENV['USE_FRAMEWORKS'] != nil
return
end
framework_build_type = target_definition.build_type.to_s
Pod::UI.puts("Framework build type is #{framework_build_type}")
if framework_build_type === "static framework"
ENV['USE_FRAMEWORKS'] = 'static'
elsif framework_build_type === "dynamic framework"
ENV['USE_FRAMEWORKS'] = 'dynamic'
else
ENV['USE_FRAMEWORKS'] = nil
end
end
def self.create_header_search_path_for_frameworks(base_folder, pod_name, framework_name, additional_paths, include_base_path = true)
search_paths = []
# When building using the prebuilt rncore we can't use framework folders as search paths since these aren't created
# Except for when adding search path for ReactCodegen since it contains source code.
if ReactNativeCoreUtils.build_rncore_from_source() || pod_name === "ReactCodegen"
platforms = $RN_PLATFORMS != nil ? $RN_PLATFORMS : []
if platforms.empty?() || platforms.length() == 1
base_path = File.join("${#{base_folder}}", pod_name, "#{framework_name}.framework", "Headers")
self.add_search_path_to_result(search_paths, base_path, additional_paths, include_base_path)
else
platforms.each { |platform|
base_path = File.join("${#{base_folder}}", "#{pod_name}-#{platform}", "#{framework_name}.framework", "Headers")
self.add_search_path_to_result(search_paths, base_path, additional_paths, include_base_path)
}
end
else
base_path = File.join("${PODS_ROOT}", "#{pod_name}")
self.add_search_path_to_result(search_paths, base_path, additional_paths, include_base_path)
end
return search_paths
end
# Add a new dependency to an existing spec, configuring also the headers search paths
def self.add_dependency(spec, dependency_name, base_folder_for_frameworks, framework_name, additional_paths: [], version: nil, subspec_dependency: nil)
# Update Search Path
current_pod_target_xcconfig = spec.to_hash["pod_target_xcconfig"] ? spec.to_hash["pod_target_xcconfig"] : {}
optional_current_search_path = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"]
current_search_paths = (optional_current_search_path != nil ? optional_current_search_path : "")
.split(" ")
create_header_search_path_for_frameworks(base_folder_for_frameworks, dependency_name, framework_name, additional_paths)
.each { |path|
wrapped_path = "\"#{path}\""
current_search_paths << wrapped_path
}
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = current_search_paths.join(" ")
spec.pod_target_xcconfig = current_pod_target_xcconfig
actual_dependency = subspec_dependency != nil ? "#{dependency_name}/#{subspec_dependency}" : dependency_name
# Set Dependency
if !version
spec.dependency actual_dependency
else
spec.dependency actual_dependency, version
end
end
def self.update_search_paths(installer)
return if ENV['USE_FRAMEWORKS'] == nil
projects = self.extract_projects(installer)
projects.each do |project|
project.build_configurations.each do |config|
header_search_paths = config.build_settings["HEADER_SEARCH_PATHS"] ||= "$(inherited)"
ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCommon", "ReactCommon", ["react/nativemodule/core"])
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-runtimeexecutor", "React_runtimeexecutor", ["platform/ios"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCommon-Samples", "ReactCommon_Samples", ["platform/ios"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Fabric", "React_Fabric", ["react/renderer/components/view/platform/cxx"], false))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-NativeModulesApple", "React_NativeModulesApple", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-graphics", "React_graphics", ["react/renderer/graphics/platform/ios"]))
.each{ |search_path|
header_search_paths = self.add_search_path_if_not_included(header_search_paths, search_path)
}
config.build_settings["HEADER_SEARCH_PATHS"] = header_search_paths
end
project.save()
end
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
if self.react_native_pods.include?(pod_name) || pod_name.include?("Pod") || pod_name.include?("Tests")
next
end
self.set_rctfolly_search_paths(target_installation_result)
self.set_codegen_search_paths(target_installation_result)
self.set_reactcommon_searchpaths(target_installation_result)
self.set_rctfabric_search_paths(target_installation_result)
self.set_imagemanager_search_path(target_installation_result)
end
end
def self.updateOSDeploymentTarget(installer)
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.native_target.build_configurations.each do |config|
old_iphone_deploy_target = config.build_settings["IPHONEOS_DEPLOYMENT_TARGET"] ?
config.build_settings["IPHONEOS_DEPLOYMENT_TARGET"] :
Helpers::Constants.min_ios_version_supported
config.build_settings["IPHONEOS_DEPLOYMENT_TARGET"] = [Helpers::Constants.min_ios_version_supported.to_f, old_iphone_deploy_target.to_f].max.to_s
end
end
end
def self.set_dynamic_frameworks_flags(installer)
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
# Set "RCT_DYNAMIC_FRAMEWORKS=1" if pod are installed with USE_FRAMEWORKS=dynamic
# This helps with backward compatibility.
if pod_name == 'React-RCTFabric' && ENV['USE_FRAMEWORKS'] == 'dynamic'
Pod::UI.puts "Setting -DRCT_DYNAMIC_FRAMEWORKS=1 to React-RCTFabric".green
rct_dynamic_framework_flag = " -DRCT_DYNAMIC_FRAMEWORKS=1"
target_installation_result.native_target.build_configurations.each do |config|
prev_build_settings = config.build_settings['OTHER_CPLUSPLUSFLAGS'] != nil ? config.build_settings['OTHER_CPLUSPLUSFLAGS'] : "$(inherited)"
config.build_settings['OTHER_CPLUSPLUSFLAGS'] = prev_build_settings + rct_dynamic_framework_flag
end
end
end
end
# ========= #
# Utilities #
# ========= #
def self.extract_projects(installer)
return installer.aggregate_targets
.map{ |t| t.user_project }
.uniq{ |p| p.path }
.push(installer.pods_project)
end
def self.safe_init(config, setting_name)
old_config = config.build_settings[setting_name]
if old_config == nil
config.build_settings[setting_name] ||= '$(inherited) '
end
end
def self.add_value_to_setting_if_missing(config, setting_name, value)
old_config = config.build_settings[setting_name]
if old_config.is_a?(Array)
old_config = old_config.join(" ")
end
trimmed_value = value.strip()
if !old_config.include?(trimmed_value)
config.build_settings[setting_name] = "#{old_config.strip()} #{trimmed_value}".strip()
end
end
def self.remove_value_from_setting_if_present(config, setting_name, value)
old_config = config.build_settings[setting_name]
if old_config.is_a?(Array)
old_config = old_config.join(" ")
end
trimmed_value = value.strip()
if old_config.include?(trimmed_value)
new_config = old_config.gsub(trimmed_value, "")
config.build_settings[setting_name] = new_config.strip()
end
end
def self.parse_xcode_version(version_string)
# The output of xcodebuild -version is something like
# Xcode 15.0
# or
# Xcode 14.3.1
# We want to capture the version digits
match = version_string.match(/(\d+)\.(\d+)(?:\.(\d+))?/)
return nil if match.nil?
return {"str" => match[0], "major" => match[1].to_i, "minor" => match[2].to_i};
end
def self.check_minimum_required_xcode(xcodebuild_manager: Xcodebuild)
version = self.parse_xcode_version(xcodebuild_manager.version)
if (version.nil? || !Gem::Version::correct?(version["str"]))
Pod::UI.warn "Unexpected XCode version string '#{xcodebuild_manager.version}'"
return
end
current = version["str"]
min_required = Helpers::Constants.min_xcode_version_supported
if Gem::Version::new(current) < Gem::Version::new(min_required)
Pod::UI.puts "React Native requires XCode >= #{min_required}. Found #{current}.".red
raise "Please upgrade XCode"
end
end
def self.add_compiler_flag_to_project(installer, flag, configuration: nil)
projects = self.extract_projects(installer)
projects.each do |project|
project.build_configurations.each do |config|
self.set_flag_in_config(config, flag, configuration: configuration)
end
project.save()
end
end
def self.remove_compiler_flag_from_project(installer, flag, configuration: nil)
projects = self.extract_projects(installer)
projects.each do |project|
project.build_configurations.each do |config|
self.remove_flag_in_config(config, flag, configuration: configuration)
end
project.save()
end
end
def self.add_compiler_flag_to_pods(installer, flag, configuration: nil)
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
target_installation_result.native_target.build_configurations.each do |config|
self.set_flag_in_config(config, flag, configuration: configuration)
end
end
end
def self.set_flag_in_config(config, flag, configuration: nil)
if configuration == nil || config.name == configuration
self.add_flag_for_key(config, flag, "OTHER_CFLAGS")
self.add_flag_for_key(config, flag, "OTHER_CPLUSPLUSFLAGS")
end
end
def self.remove_flag_in_config(config, flag, configuration: nil)
if configuration == nil || config.name == configuration
self.remove_flag_for_key(config, flag, "OTHER_CFLAGS")
self.remove_flag_for_key(config, flag, "OTHER_CPLUSPLUSFLAGS")
end
end
def self.add_flag_for_key(config, flag, key)
current_setting = config.build_settings[key] ? config.build_settings[key] : "$(inherited)"
if current_setting.kind_of?(Array)
current_setting = current_setting
.map { |s| s.gsub('"', '') }
.map { |s| s.gsub('\"', '') }
.join(" ")
end
if !current_setting.include?(flag)
current_setting = "#{current_setting} #{flag}"
end
config.build_settings[key] = current_setting
end
def self.remove_flag_for_key(config, flag, key)
current_setting = config.build_settings[key] ? config.build_settings[key] : "$(inherited)"
if current_setting.kind_of?(Array)
current_setting = current_setting
.map { |s| s.gsub('"', '') }
.map { |s| s.gsub('\"', '') }
.join(" ")
end
if current_setting.include?(flag)
current_setting.slice! flag
current_setting.strip!
end
config.build_settings[key] = current_setting
end
def self.add_search_path_if_not_included(current_search_paths, new_search_path)
new_search_path = new_search_path.strip
if current_search_paths.is_a?(String)
current_search_paths = current_search_paths.strip
return "#{current_search_paths} #{new_search_path}" unless current_search_paths.include?(new_search_path)
end
if current_search_paths.is_a?(Array)
current_search_paths = current_search_paths.map(&:strip)
return current_search_paths << new_search_path unless current_search_paths.include?(new_search_path)
end
current_search_paths
end
def self.update_header_paths_if_depends_on(target_installation_result, dependency_name, header_paths)
depends_on_framework = target_installation_result.native_target.dependencies.any? { |d| d.name == dependency_name }
if depends_on_framework
target_installation_result.native_target.build_configurations.each do |config|
header_search_path = config.build_settings["HEADER_SEARCH_PATHS"] != nil ? config.build_settings["HEADER_SEARCH_PATHS"] : "$(inherited)"
header_paths.each { |header| header_search_path = ReactNativePodsUtils.add_search_path_if_not_included(header_search_path, header) }
config.build_settings["HEADER_SEARCH_PATHS"] = header_search_path
end
end
end
def self.set_rctfolly_search_paths(target_installation_result)
ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "RCT-Folly", [
"\"$(PODS_ROOT)/RCT-Folly\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/fast_float/include\"",
"\"$(PODS_ROOT)/fmt/include\"",
"\"$(PODS_ROOT)/boost\""
])
end
def self.set_codegen_search_paths(target_installation_result)
header_search_paths = ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCodegen", "ReactCodegen", [])
.map { |search_path| "\"#{search_path}\"" }
ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "ReactCodegen", header_search_paths)
end
def self.set_reactcommon_searchpaths(target_installation_result)
header_search_paths = ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCommon", "ReactCommon", ["react/nativemodule/core"])
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-runtimeexecutor", "React_runtimeexecutor", ["platform/ios"]))
.map { |search_path| "\"#{search_path}\"" }
ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "ReactCommon", header_search_paths)
end
def self.set_rctfabric_search_paths(target_installation_result)
header_search_paths = ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-RCTFabric", "RCTFabric", [])
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Fabric", "React_Fabric", ["react/renderer/components/view/platform/cxx"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-FabricImage", "React_FabricImage", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Graphics", "React_graphics", ["react/renderer/graphics/platform/ios"]))
.map { |search_path| "\"#{search_path}\"" }
ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "React-RCTFabric", header_search_paths)
end
def self.set_imagemanager_search_path(target_installation_result)
header_search_paths = ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Fabric", "React_Fabric", ["react/renderer/imagemanager/platform/ios"])
.map { |search_path| "\"#{search_path}\"" }
ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "React-ImageManager", header_search_paths)
end
def self.react_native_pods
pods = [
"DoubleConversion",
"FBLazyVector",
"RCT-Folly",
"RCTRequired",
"RCTTypeSafety",
"React",
"ReactCodegen",
"React-Core",
"React-CoreModules",
"React-Fabric",
"React-FabricImage",
"React-ImageManager",
"React-RCTActionSheet",
"React-RCTAnimation",
"React-RCTAppDelegate",
"React-RCTBlob",
"React-RCTFabric",
"React-RCTRuntime",
"React-RCTImage",
"React-RCTLinking",
"React-RCTNetwork",
"React-RCTPushNotification",
"React-RCTSettings",
"React-RCTText",
"React-RCTTest",
"React-RCTVibration",
"React-callinvoker",
"React-cxxreact",
"React-graphics",
"React-jsi",
"React-jsiexecutor",
"React-jsinspector",
"React-jsitooling",
"React-logger",
"React-oscompat",
"React-perflogger",
"React-runtimeexecutor",
"React-timing",
"ReactCommon",
"Yoga",
"boost",
"fast_float",
"fmt",
"glog",
"hermes-engine",
"React-hermes",
]
if ENV['USE_THIRD_PARTY_JSC'] != '1'
pods << "React-jsc"
end
return pods
end
def self.add_search_path_to_result(result, base_path, additional_paths, include_base_path)
if (include_base_path)
result << base_path
end
additional_paths.each { |extra_path|
result << File.join(base_path, extra_path)
}
return result
end
def self.add_ndebug_flag_to_pods_in_release(installer)
ndebug_flag = " -DNDEBUG"
installer.aggregate_targets.each do |aggregate_target|
aggregate_target.xcconfigs.each do |config_name, config_file|
is_release = aggregate_target.user_build_configurations[config_name] == :release
unless is_release
next
end
self.add_flag_to_map_with_inheritance(config_file.attributes, 'OTHER_CPLUSPLUSFLAGS', ndebug_flag);
self.add_flag_to_map_with_inheritance(config_file.attributes, 'OTHER_CFLAGS', ndebug_flag);
xcconfig_path = aggregate_target.xcconfig_path(config_name)
config_file.save_as(xcconfig_path)
end
end
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
target_installation_result.native_target.build_configurations.each do |config|
is_release = config.type == :release
unless is_release
next
end
self.add_flag_to_map_with_inheritance(config.build_settings, 'OTHER_CPLUSPLUSFLAGS', ndebug_flag);
self.add_flag_to_map_with_inheritance(config.build_settings, 'OTHER_CFLAGS', ndebug_flag);
end
end
end
def self.add_flag_to_map_with_inheritance(map, field, flag)
if map[field] == nil
map[field] = "$(inherited)" + flag
else
unless map[field].include?(flag)
if map[field].instance_of? String
map[field] = map[field] + flag
elsif map[field].instance_of? Array
map[field].push(flag)
end
end
unless map[field].include?("$(inherited)")
if map[field].instance_of? String
map[field] = "$(inherited) " + map[field]
elsif map[field].instance_of? Array
map[field].unshift("$(inherited)")
end
end
end
end
def self.resolve_use_frameworks(spec, header_mappings_dir: nil, module_name: nil)
return unless ENV['USE_FRAMEWORKS']
if module_name
spec.module_name = module_name
end
if header_mappings_dir != nil && ReactNativeCoreUtils.build_rncore_from_source()
spec.header_mappings_dir = header_mappings_dir
end
end
end

View File

@@ -0,0 +1,56 @@
/**
* 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';
/**
* Wrapper required to abstract away from the actual codegen.
* This is needed because, when running tests in Sandcastle, not everything is setup as usually.
* For example, the `@react-native/codegen` lib is not present.
*
* Thanks to this wrapper, we are able to mock the getter for the codegen in a way that allow us to return
* a custom object which mimics the Codegen interface.
*
* @return an object that can generate the code for the New Architecture.
*/
function getCodegen() /*: $FlowFixMe */ {
let RNCodegen;
try {
// $FlowFixMe[cannot-resolve-module]
RNCodegen = require('../../packages/react-native-codegen/lib/generators/RNCodegen.js');
} catch (e) {
// $FlowFixMe[cannot-resolve-module]
RNCodegen = require('@react-native/codegen/lib/generators/RNCodegen.js');
}
if (!RNCodegen) {
throw new Error('RNCodegen not found.');
}
return RNCodegen;
}
function getCombineJSToSchema() /*: $FlowFixMe */ {
let combineJSToSchema;
try {
// $FlowFixMe[cannot-resolve-module]
combineJSToSchema = require('../../packages/react-native-codegen/lib/cli/combine/combine-js-to-schema.js');
} catch (e) {
// $FlowFixMe[cannot-resolve-module]
combineJSToSchema = require('@react-native/codegen/lib/cli/combine/combine-js-to-schema.js');
}
if (!combineJSToSchema) {
throw new Error('combine-js-to-schema not found.');
}
return combineJSToSchema;
}
module.exports = {
getCodegen,
getCombineJSToSchema,
};

View File

@@ -0,0 +1,74 @@
/**
* 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';
const fs = require('fs');
const path = require('path');
const REACT_NATIVE_REPOSITORY_ROOT = path.join(
__dirname,
'..',
'..',
'..',
'..',
'..',
);
const REACT_NATIVE_PACKAGE_ROOT_FOLDER /*: string */ = path.join(
__dirname,
'..',
'..',
'..',
);
const CODEGEN_REPO_PATH = `${REACT_NATIVE_REPOSITORY_ROOT}/packages/react-native-codegen`;
const CORE_LIBRARIES_WITH_OUTPUT_FOLDER /*: {[string]: $FlowFixMe} */ = {
FBReactNativeSpec: {
ios: path.join(
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
'React',
'FBReactNativeSpec',
) /*:: as string */,
android: path.join(
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
'ReactAndroid',
'build',
'generated',
'source',
'codegen',
) /*:: as string */,
},
};
const packageJsonPath = path.join(
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
'package.json',
);
// $FlowFixMe[signature-verification-failure]
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const REACT_NATIVE = packageJson.name;
const TEMPLATES_FOLDER_PATH /*: string */ = path.join(
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
'scripts',
'codegen',
'templates',
);
module.exports = {
CODEGEN_REPO_PATH,
CORE_LIBRARIES_WITH_OUTPUT_FOLDER,
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
REACT_NATIVE,
TEMPLATES_FOLDER_PATH,
packageJson,
};

View File

@@ -0,0 +1,67 @@
/**
* 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';
const {TEMPLATES_FOLDER_PATH, packageJson} = require('./constants');
const {codegenLog} = require('./utils');
const fs = require('fs');
const path = require('path');
const APP_DEPENDENCY_PROVIDER_H_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTAppDependencyProviderH.template',
);
const APP_DEPENDENCY_PROVIDER_MM_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTAppDependencyProviderMM.template',
);
const APP_DEPENDENCY_PROVIDER_PODSPEC_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'ReactAppDependencyProvider.podspec.template',
);
function generateAppDependencyProvider(outputDir /*: string */) {
fs.mkdirSync(outputDir, {recursive: true});
codegenLog('Generating RCTAppDependencyProvider');
const templateH = fs.readFileSync(
APP_DEPENDENCY_PROVIDER_H_TEMPLATE_PATH,
'utf8',
);
const finalPathH = path.join(outputDir, 'RCTAppDependencyProvider.h');
fs.writeFileSync(finalPathH, templateH);
codegenLog(`Generated artifact: ${finalPathH}`);
const templateMM = fs.readFileSync(
APP_DEPENDENCY_PROVIDER_MM_TEMPLATE_PATH,
'utf8',
);
const finalPathMM = path.join(outputDir, 'RCTAppDependencyProvider.mm');
fs.writeFileSync(finalPathMM, templateMM);
codegenLog(`Generated artifact: ${finalPathMM}`);
// Generate the podspec file
const templatePodspec = fs
.readFileSync(APP_DEPENDENCY_PROVIDER_PODSPEC_TEMPLATE_PATH, 'utf8')
.replace(/{react-native-version}/, packageJson.version)
.replace(/{react-native-licence}/, packageJson.license);
const finalPathPodspec = path.join(
outputDir,
'ReactAppDependencyProvider.podspec',
);
fs.writeFileSync(finalPathPodspec, templatePodspec);
codegenLog(`Generated podspec: ${finalPathPodspec}`);
}
module.exports = {
generateAppDependencyProvider,
};

View File

@@ -0,0 +1,122 @@
/**
* 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';
const {TEMPLATES_FOLDER_PATH} = require('./constants');
const {parseiOSAnnotations} = require('./utils');
const fs = require('fs');
const path = require('path');
const MODULES_PROTOCOLS_H_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTModulesConformingToProtocolsProviderH.template',
);
const MODULES_PROTOCOLS_MM_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTModulesConformingToProtocolsProviderMM.template',
);
function generateCustomURLHandlers(
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
outputDir /*: string */,
) {
const iosAnnotations = parseiOSAnnotations(libraries);
const imageURLLoaderModules = new Set /*::<string>*/();
const imageDataDecoderModules = new Set /*::<string>*/();
const urlRequestHandlersModules = new Set /*::<string>*/();
// $FlowFixMe[missing-local-annot]]
const wrapInArrayIfNecessary = value =>
Array.isArray(value) || value == null ? value : [value];
// Old API
for (const library of libraries) {
const modulesConformingToProtocol =
library?.config?.ios?.modulesConformingToProtocol;
if (modulesConformingToProtocol == null) {
continue;
}
wrapInArrayIfNecessary(
modulesConformingToProtocol.RCTImageURLLoader,
)?.forEach(moduleName => {
imageURLLoaderModules.add(moduleName);
});
wrapInArrayIfNecessary(
modulesConformingToProtocol.RCTImageDataDecoder,
)?.forEach(moduleName => {
imageDataDecoderModules.add(moduleName);
});
wrapInArrayIfNecessary(
modulesConformingToProtocol.RCTURLRequestHandler,
)?.forEach(moduleName => {
urlRequestHandlersModules.add(moduleName);
});
}
// New API
for (const {modules: moduleAnnotationMap} of Object.values(iosAnnotations)) {
for (const [moduleName, annotation] of Object.entries(
moduleAnnotationMap,
)) {
const conformsToProtocols = annotation.conformsToProtocols;
if (!conformsToProtocols) {
continue;
}
if (conformsToProtocols.includes('RCTImageURLLoader')) {
imageURLLoaderModules.add(moduleName);
}
if (conformsToProtocols.includes('RCTImageDataDecoder')) {
imageDataDecoderModules.add(moduleName);
}
if (conformsToProtocols.includes('RCTURLRequestHandler')) {
urlRequestHandlersModules.add(moduleName);
}
}
}
const customImageURLLoaderClasses = Array.from(imageURLLoaderModules)
.map(className => `@"${className}"`)
.join(',\n\t\t');
const customImageDataDecoderClasses = Array.from(imageDataDecoderModules)
.map(className => `@"${className}"`)
.join(',\n\t\t');
const customURLHandlerClasses = Array.from(urlRequestHandlersModules)
.map(className => `@"${className}"`)
.join(',\n\t\t');
const template = fs.readFileSync(MODULES_PROTOCOLS_MM_TEMPLATE_PATH, 'utf8');
const finalMMFile = template
.replace(/{imageURLLoaderClassNames}/, customImageURLLoaderClasses)
.replace(/{imageDataDecoderClassNames}/, customImageDataDecoderClasses)
.replace(/{requestHandlersClassNames}/, customURLHandlerClasses);
fs.mkdirSync(outputDir, {recursive: true});
fs.writeFileSync(
path.join(outputDir, 'RCTModulesConformingToProtocolsProvider.mm'),
finalMMFile,
);
const templateH = fs.readFileSync(MODULES_PROTOCOLS_H_TEMPLATE_PATH, 'utf8');
fs.writeFileSync(
path.join(outputDir, 'RCTModulesConformingToProtocolsProvider.h'),
templateH,
);
}
module.exports = {
generateCustomURLHandlers,
};

View File

@@ -0,0 +1,43 @@
/**
* 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';
const {generateCode} = require('./generateNativeCode');
const {generateSchemaInfo} = require('./generateSchemaInfos');
const {
buildCodegenIfNeeded,
findProjectRootLibraries,
readPkgJsonInDirectory,
} = require('./utils');
function generateFBReactNativeSpecIOS(projectRoot /*: string */) /*: void*/ {
const platform = 'ios';
buildCodegenIfNeeded();
const pkgJson = readPkgJsonInDirectory(projectRoot);
const fbReactNativeSpecLib = findProjectRootLibraries(
pkgJson,
projectRoot,
).filter(library => library.config.name === 'FBReactNativeSpec')[0];
if (!fbReactNativeSpecLib) {
throw new Error(
"[Codegen] Can't find FBReactNativeSpec library. Failed to generate artifacts",
);
}
const fbReactNativeSchemaInfo = generateSchemaInfo(
fbReactNativeSpecLib,
platform,
);
generateCode('', fbReactNativeSchemaInfo, false, platform);
}
module.exports = {
generateFBReactNativeSpecIOS,
};

View File

@@ -0,0 +1,106 @@
/**
* 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';
const generateSpecsCLIExecutor = require('../generate-specs-cli-executor');
const {CORE_LIBRARIES_WITH_OUTPUT_FOLDER} = require('./constants');
const {codegenLog} = require('./utils');
const fs = require('fs');
const os = require('os');
const path = require('path');
function generateNativeCode(
outputPath /*: string */,
schemaInfos /*: $ReadOnlyArray<$FlowFixMe> */,
includesGeneratedCode /*: boolean */,
platform /*: string */,
) /*: Array<void> */ {
return schemaInfos.map(schemaInfo => {
generateCode(outputPath, schemaInfo, includesGeneratedCode, platform);
});
}
function generateCode(
outputPath /*: string */,
schemaInfo /*: $FlowFixMe */,
includesGeneratedCode /*: boolean */,
platform /*: string */,
) {
if (shouldSkipGenerationForFBReactNativeSpec(schemaInfo, platform)) {
codegenLog(
'[Codegen - FBReactNativeSpec] Skipping iOS code generation for FBReactNativeSpec as it has been generated already.',
true,
);
return;
}
const libraryName = schemaInfo.library.config.name;
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), libraryName));
const tmpOutputDir = path.join(tmpDir, 'out');
fs.mkdirSync(tmpOutputDir, {recursive: true});
codegenLog(`Generating Native Code for ${libraryName} - ${platform}`);
const useLocalIncludePaths = includesGeneratedCode;
generateSpecsCLIExecutor.generateSpecFromInMemorySchema(
platform,
schemaInfo.schema,
tmpOutputDir,
libraryName,
'com.facebook.fbreact.specs',
schemaInfo.library.config.type,
useLocalIncludePaths,
);
// Finally, copy artifacts to the final output directory.
const outputDir =
reactNativeCoreLibraryOutputPath(libraryName, platform) ?? outputPath;
fs.mkdirSync(outputDir, {recursive: true});
// $FlowFixMe[prop-missing] - `fs.cpSync` is missing in Flow libdefs.
fs.cpSync(tmpOutputDir, outputDir, {recursive: true});
codegenLog(`Generated artifacts: ${outputDir}`);
}
function reactNativeCoreLibraryOutputPath(
libraryName /*: string */,
platform /*: string */,
) {
return CORE_LIBRARIES_WITH_OUTPUT_FOLDER[libraryName]
? CORE_LIBRARIES_WITH_OUTPUT_FOLDER[libraryName][platform]
: null;
}
function shouldSkipGenerationForFBReactNativeSpec(
schemaInfo /*: $FlowFixMe */,
platform /*: string */,
) {
if (
platform !== 'ios' ||
schemaInfo.library.config.name !== 'FBReactNativeSpec'
) {
return false;
}
const fbReactNativeSpecOutputPath =
CORE_LIBRARIES_WITH_OUTPUT_FOLDER.FBReactNativeSpec.ios;
const fbReactNativeSpecAbsolutePath = path.resolve(
fbReactNativeSpecOutputPath,
);
return (
fbReactNativeSpecAbsolutePath.includes('node_modules') &&
fs.existsSync(fbReactNativeSpecAbsolutePath) &&
fs.readdirSync(fbReactNativeSpecAbsolutePath).length > 0
);
}
module.exports = {
generateNativeCode,
generateCode,
};

View File

@@ -0,0 +1,44 @@
/**
* 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';
const {TEMPLATES_FOLDER_PATH} = require('./constants');
const {codegenLog} = require('./utils');
const fs = require('fs');
const path = require('path');
const PACKAGE_SWIFT_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'Package.swift.template',
);
function generatePackageSwift(
projectRoot /*: string */,
outputDir /*: string */,
reactNativePath /*: string */,
) {
const fullOutputPath = path.join(projectRoot, outputDir);
fs.mkdirSync(outputDir, {recursive: true});
// Generate PAckage.swift File
codegenLog('Generating Package.swift');
const templateH = fs
.readFileSync(PACKAGE_SWIFT_TEMPLATE_PATH, 'utf8')
.replace(
/{reactNativePath}/,
path.relative(fullOutputPath, reactNativePath),
);
const finalPathH = path.join(outputDir, 'Package.swift');
fs.writeFileSync(finalPathH, templateH);
codegenLog(`Generated artifact: ${finalPathH}`);
}
module.exports = {
generatePackageSwift,
};

View File

@@ -0,0 +1,121 @@
/**
* 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';
const {TEMPLATES_FOLDER_PATH} = require('./constants');
const {
codegenLog,
isReactNativeCoreLibrary,
parseiOSAnnotations,
} = require('./utils');
const fs = require('fs');
const path = require('path');
const MODULE_PROVIDERS_H_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTModuleProvidersH.template',
);
const MODULE_PROVIDERS_MM_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTModuleProvidersMM.template',
);
function generateRCTModuleProviders(
projectRoot /*: string */,
pkgJson /*: $FlowFixMe */,
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
outputDir /*: string */,
) {
fs.mkdirSync(outputDir, {recursive: true});
// Generate Header File
codegenLog('Generating RCTModulesProvider.h');
const templateH = fs.readFileSync(MODULE_PROVIDERS_H_TEMPLATE_PATH, 'utf8');
const finalPathH = path.join(outputDir, 'RCTModuleProviders.h');
fs.writeFileSync(finalPathH, templateH);
codegenLog(`Generated artifact: ${finalPathH}`);
codegenLog('Generating RCTModuleProviders.mm');
let modulesInLibraries /*: {[string]: Array<$FlowFixMe>} */ = {};
let app = pkgJson.codegenConfig
? {config: pkgJson.codegenConfig, libraryPath: projectRoot}
: null;
const moduleLibraries = libraries
.concat(app)
.filter(Boolean)
.filter(({config, libraryPath}) => {
if (
isReactNativeCoreLibrary(config.name) ||
config.type === 'components'
) {
return false;
}
return true;
});
// Old API
moduleLibraries.forEach(({config, libraryPath}) => {
const libraryName = JSON.parse(
fs.readFileSync(path.join(libraryPath, 'package.json'), 'utf8'),
).name;
if (config.ios?.modulesProvider) {
modulesInLibraries[libraryName] = Object.keys(
config.ios?.modulesProvider,
).map(moduleName => {
return {
moduleName,
className: config.ios?.modulesProvider[moduleName],
};
});
}
});
// New API
const iosAnnotations = parseiOSAnnotations(moduleLibraries);
for (const [libraryName, {modules: moduleAnnotationMap}] of Object.entries(
iosAnnotations,
)) {
for (const [moduleName, annotation] of Object.entries(
moduleAnnotationMap,
)) {
if (annotation.className) {
modulesInLibraries[libraryName] = modulesInLibraries[libraryName] || [];
modulesInLibraries[libraryName].push({
moduleName,
className: annotation.className,
});
}
}
}
const modulesMapping = Object.keys(modulesInLibraries)
.flatMap(library => {
const modules = modulesInLibraries[library];
return modules.map(({moduleName, className}) => {
return `\t\t@"${moduleName}": @"${className}", // ${library}`;
});
})
.join('\n');
// Generate implementation file
const templateMM = fs
.readFileSync(MODULE_PROVIDERS_MM_TEMPLATE_PATH, 'utf8')
.replace(/{moduleMapping}/, modulesMapping);
const finalPathMM = path.join(outputDir, 'RCTModuleProviders.mm');
fs.writeFileSync(finalPathMM, templateMM);
codegenLog(`Generated artifact: ${finalPathMM}`);
}
module.exports = {
generateRCTModuleProviders,
};

View File

@@ -0,0 +1,230 @@
/**
* 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';
const {TEMPLATES_FOLDER_PATH} = require('./constants');
const {
codegenLog,
isReactNativeCoreLibrary,
parseiOSAnnotations,
} = require('./utils');
const fs = require('fs');
const path = require('path');
const THIRD_PARTY_COMPONENTS_H_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTThirdPartyComponentsProviderH.template',
);
const THIRD_PARTY_COMPONENTS_MM_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'RCTThirdPartyComponentsProviderMM.template',
);
function generateRCTThirdPartyComponents(
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
outputDir /*: string */,
) {
fs.mkdirSync(outputDir, {recursive: true});
// Generate Header File
codegenLog('Generating RCTThirdPartyComponentsProvider.h');
const templateH = fs.readFileSync(
THIRD_PARTY_COMPONENTS_H_TEMPLATE_PATH,
'utf8',
);
const finalPathH = path.join(outputDir, 'RCTThirdPartyComponentsProvider.h');
fs.writeFileSync(finalPathH, templateH);
codegenLog(`Generated artifact: ${finalPathH}`);
codegenLog('Generating RCTThirdPartyComponentsProvider.mm');
let componentsInLibraries /*: {[string]: Array<$FlowFixMe>} */ = {};
const componentLibraries = libraries.filter(({config, libraryPath}) => {
if (isReactNativeCoreLibrary(config.name) || config.type === 'modules') {
return false;
}
return true;
});
const librariesToCrawl /*: {[string]: $FlowFixMe} */ = {};
// Using new API explicitly or not using any config field to define components.
const componentLibrariesUsingNewApi = [];
const componentLibrariesUsingOldApi = [];
for (const library of componentLibraries) {
if (
library.config.ios?.components ||
!library.config.ios?.componentProvider
) {
componentLibrariesUsingNewApi.push(library);
} else {
componentLibrariesUsingOldApi.push(library);
}
}
// Old API
componentLibrariesUsingOldApi.forEach(library => {
const {config, libraryPath} = library;
const libraryName = JSON.parse(
fs.readFileSync(path.join(libraryPath, 'package.json'), 'utf8'),
).name;
librariesToCrawl[libraryName] = library;
const componentsProvider = config.ios?.componentProvider;
delete librariesToCrawl[libraryName];
componentsInLibraries[libraryName] =
componentsInLibraries[libraryName] || [];
Object.keys(componentsProvider).forEach(componentName => {
componentsInLibraries[libraryName].push({
componentName,
className: componentsProvider[componentName],
});
});
});
// New API
const iosAnnotations = parseiOSAnnotations(componentLibrariesUsingNewApi);
for (const [libraryName, annotationMap] of Object.entries(iosAnnotations)) {
const {library, components} = annotationMap;
librariesToCrawl[libraryName] = library;
for (const [componentName, annotation] of Object.entries(components)) {
if (annotation.className) {
delete librariesToCrawl[libraryName];
componentsInLibraries[libraryName] =
componentsInLibraries[libraryName] || [];
componentsInLibraries[libraryName].push({
componentName,
className: annotation.className,
});
}
}
}
Object.entries(librariesToCrawl).forEach(([libraryName, library]) => {
const {libraryPath} = library;
codegenLog(`Crawling ${libraryName} library for components`);
// crawl all files and subdirectories for file with the ".mm" extension
const files = findFilesWithExtension(libraryPath, '.mm');
const componentsMapping = files
.flatMap(file => findRCTComponentViewProtocolClass(file))
.filter(Boolean);
if (componentsMapping.length !== 0) {
codegenLog(
`[DEPRECATED] ${libraryName} should add the 'ios.componentProvider' property in their codegenConfig`,
true,
);
}
componentsInLibraries[libraryName] = componentsMapping;
});
const thirdPartyComponentsMapping = Object.keys(componentsInLibraries)
.flatMap(library => {
const components = componentsInLibraries[library];
return components.map(({componentName, className}) => {
return `\t\t@"${componentName}": NSClassFromString(@"${className}"), // ${library}`;
});
})
.join('\n');
// Generate implementation file
const templateMM = fs
.readFileSync(THIRD_PARTY_COMPONENTS_MM_TEMPLATE_PATH, 'utf8')
.replace(/{thirdPartyComponentsMapping}/, thirdPartyComponentsMapping);
const finalPathMM = path.join(
outputDir,
'RCTThirdPartyComponentsProvider.mm',
);
fs.writeFileSync(finalPathMM, templateMM);
codegenLog(`Generated artifact: ${finalPathMM}`);
}
// Given a path, return the paths of all the files with extension .mm in
// the path dir and all its subdirectories.
function findFilesWithExtension(
filePath /*: string */,
extension /*: string */,
) /*: Array<string> */ {
const files = [];
const dir = fs.readdirSync(filePath);
dir.forEach(file => {
const absolutePath = path.join(filePath, file);
// Exclude files provided by react-native
if (absolutePath.includes(`${path.sep}react-native${path.sep}`)) {
return null;
}
// Skip hidden folders, that starts with `.` but allow `.pnpm`
if (
absolutePath.includes(`${path.sep}.`) &&
!absolutePath.includes(`${path.sep}.pnpm`)
) {
return null;
}
if (
fs.existsSync(absolutePath) &&
fs.statSync(absolutePath).isDirectory()
) {
files.push(...findFilesWithExtension(absolutePath, extension));
} else if (file.endsWith(extension)) {
files.push(absolutePath);
}
});
return files;
}
// Given a filepath, read the file and look for a string that starts with 'Class<RCTComponentViewProtocol> '
// and ends with 'Cls(void)'. Return the string between the two.
function findRCTComponentViewProtocolClass(filepath /*: string */) {
const fileContent = fs.readFileSync(filepath, 'utf8');
const regex = /Class<RCTComponentViewProtocol> (.*)Cls\(/;
const match = fileContent.match(regex);
if (match) {
const componentName = match[1];
// split the file by \n
// remove all the lines before the one that matches the regex above
// find the first return statement after that that ends with .class
// return what's between return and `.class`
const lines = fileContent.split('\n');
const signatureIndex = lines.findIndex(line => regex.test(line));
const returnRegex = /return (.*)\.class/;
const classNameMatch = String(lines.slice(signatureIndex).join('\n')).match(
returnRegex,
);
if (classNameMatch) {
const className = classNameMatch[1];
codegenLog(`Match found ${componentName} -> ${className}`);
return {
componentName,
className,
};
}
console.warn(
`Could not find class name for component ${componentName}. Register it manually`,
);
return null;
}
return null;
}
module.exports = {
generateRCTThirdPartyComponents,
};

View File

@@ -0,0 +1,103 @@
/**
* 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';
const {
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
TEMPLATES_FOLDER_PATH,
packageJson,
} = require('./constants');
const {codegenLog} = require('./utils');
const {execSync} = require('child_process');
const fs = require('fs');
const path = require('path');
const REACT_CODEGEN_PODSPEC_TEMPLATE_PATH = path.join(
TEMPLATES_FOLDER_PATH,
'ReactCodegen.podspec.template',
);
function generateReactCodegenPodspec(
appPath /*: string */,
appPkgJson /*: $FlowFixMe */,
outputPath /*: string */,
baseOutputPath /*: string */,
) {
const inputFiles = getInputFiles(appPath, appPkgJson);
const codegenScript = codegenScripts(appPath, baseOutputPath);
const template = fs.readFileSync(REACT_CODEGEN_PODSPEC_TEMPLATE_PATH, 'utf8');
const finalPodspec = template
.replace(/{react-native-version}/, packageJson.version)
.replace(/{input-files}/, inputFiles)
.replace(/{codegen-script}/, codegenScript);
const finalPathPodspec = path.join(outputPath, 'ReactCodegen.podspec');
fs.writeFileSync(finalPathPodspec, finalPodspec);
codegenLog(`Generated podspec: ${finalPathPodspec}`);
}
function getInputFiles(appPath /*: string */, appPkgJson /*: $FlowFixMe */) {
const jsSrcsDir = appPkgJson.codegenConfig?.jsSrcsDir;
if (!jsSrcsDir) {
return '[]';
}
const xcodeproj = String(
execSync(`find ${appPath} -type d -name "*.xcodeproj"`),
)
.trim()
.split('\n')
.filter(
projectPath =>
!projectPath.includes('/Pods/') && // exclude Pods/Pods.xcodeproj
!projectPath.includes('/node_modules/'), // exclude all the xcodeproj in node_modules of libraries
)[0];
if (!xcodeproj) {
throw new Error(
`Cannot find .xcodeproj file inside ${appPath}. This is required to determine codegen spec paths relative to native project.`,
);
}
const jsFiles = '-name "Native*.js" -or -name "*NativeComponent.js"';
const tsFiles = '-name "Native*.ts" -or -name "*NativeComponent.ts"';
const findCommand = `find ${path.join(appPath, jsSrcsDir)} -type f -not -path "*/__mocks__/*" -and \\( ${jsFiles} -or ${tsFiles} \\)`;
const list = String(execSync(findCommand))
.trim()
.split('\n')
.sort()
.map(filepath => `"\${PODS_ROOT}/${path.relative(xcodeproj, filepath)}"`)
.join(',\n');
return `[${list}]`;
}
function codegenScripts(appPath /*: string */, baseOutputPath /*: string */) {
const relativeAppPath = path.relative(baseOutputPath, appPath);
const relativeReactNativeRootFolder = path.relative(
baseOutputPath,
REACT_NATIVE_PACKAGE_ROOT_FOLDER,
);
return `<<-SCRIPT
pushd "$PODS_ROOT/../" > /dev/null
RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd)
popd >/dev/null
export RCT_SCRIPT_RN_DIR="$RCT_SCRIPT_POD_INSTALLATION_ROOT/${relativeReactNativeRootFolder}"
export RCT_SCRIPT_APP_PATH="$RCT_SCRIPT_POD_INSTALLATION_ROOT/${relativeAppPath.length === 0 ? '.' : relativeAppPath}"
export RCT_SCRIPT_OUTPUT_DIR="$RCT_SCRIPT_POD_INSTALLATION_ROOT"
export RCT_SCRIPT_TYPE="withCodegenDiscovery"
export SCRIPT_PHASES_SCRIPT="$RCT_SCRIPT_RN_DIR/scripts/react_native_pods_utils/script_phases.sh"
export WITH_ENVIRONMENT="$RCT_SCRIPT_RN_DIR/scripts/xcode/with-environment.sh"
/bin/sh -c '"$WITH_ENVIRONMENT" "$SCRIPT_PHASES_SCRIPT"'
SCRIPT`;
}
module.exports = {
generateReactCodegenPodspec,
};

View File

@@ -0,0 +1,123 @@
/**
* 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';
const CodegenUtils = require('../codegen-utils');
const {codegenLog} = require('./utils');
const fs = require('fs');
const glob = require('glob');
const path = require('path');
function generateSchemaInfos(
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
) /*: Array<$FlowFixMe> */ {
// $FlowFixMe[incompatible-type]
return libraries.map(generateSchemaInfo);
}
function generateSchemaInfo(
library /*: $FlowFixMe */,
platform /*: string */,
) /*: $FlowFixMe */ {
const pathToJavaScriptSources = path.join(
library.libraryPath,
library.config.jsSrcsDir,
);
codegenLog(`Processing ${library.config.name}`);
const supportedApplePlatforms = extractSupportedApplePlatforms(
library.config.name,
library.libraryPath,
);
// Generate one schema for the entire library...
return {
library: library,
supportedApplePlatforms,
schema: CodegenUtils.getCombineJSToSchema().combineSchemasInFileList(
[pathToJavaScriptSources],
platform,
/NativeSampleTurboModule/,
),
};
}
const APPLE_PLATFORMS = ['ios', 'macos', 'tvos', 'visionos'];
function extractSupportedApplePlatforms(
dependency /*: string */,
dependencyPath /*: string */,
) /*: ?{[string]: boolean} */ {
codegenLog('Searching for podspec in the project dependencies.', true);
const podspecs = glob.sync('*.podspec', {cwd: dependencyPath});
if (podspecs.length === 0) {
return;
}
// Take the first podspec found
const podspec = fs.readFileSync(
path.join(dependencyPath, podspecs[0]),
'utf8',
);
/**
* Podspec can have platforms defined in two ways:
* 1. `spec.platforms = { :ios => "11.0", :tvos => "11.0" }`
* 2. `s.ios.deployment_target = "11.0"`
* `s.tvos.deployment_target = "11.0"`
*/
const supportedPlatforms = podspec
.split('\n')
.filter(
line => line.includes('platform') || line.includes('deployment_target'),
)
.join('');
// Generate a map of supported platforms { [platform]: true/false }
const supportedPlatformsMap = APPLE_PLATFORMS.reduce(
(acc, platform) => ({
...acc,
[platform]: supportedPlatforms.includes(
getCocoaPodsPlatformKey(platform),
),
}),
{} /*:: as {[string]: boolean} */,
);
const supportedPlatformsList = Object.keys(supportedPlatformsMap).filter(
key => supportedPlatformsMap[key],
);
if (supportedPlatformsList.length > 0) {
codegenLog(
`Supported Apple platforms: ${supportedPlatformsList.join(
', ',
)} for ${dependency}`,
);
}
return supportedPlatformsMap;
}
// Cocoapods specific platform keys
function getCocoaPodsPlatformKey(platformName /*: string */) {
if (platformName === 'macos') {
return 'osx';
}
return platformName;
}
module.exports = {
generateSchemaInfos,
generateSchemaInfo,
extractSupportedApplePlatforms,
};

View File

@@ -0,0 +1,93 @@
/**
* 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';
const {TEMPLATES_FOLDER_PATH} = require('./constants');
const {parseiOSAnnotations} = require('./utils');
const fs = require('fs');
const path = require('path');
const UNSTABLE_MODULES_REQUIRING_MAIN_QUEUE_SETUP_PROVIDER_H_TEMPLATE_PATH =
path.join(
TEMPLATES_FOLDER_PATH,
'RCTUnstableModulesRequiringMainQueueSetupProviderH.template',
);
const UNSTABLE_MODULES_REQUIRING_MAIN_QUEUE_SETUP_PROVIDER_MM_TEMPLATE_PATH =
path.join(
TEMPLATES_FOLDER_PATH,
'RCTUnstableModulesRequiringMainQueueSetupProviderMM.template',
);
function generateUnstableModulesRequiringMainQueueSetupProvider(
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
outputDir /*: string */,
) {
const iosAnnotations = parseiOSAnnotations(libraries);
const modulesRequiringMainQueueSetup = new Set /*::<string>*/();
// Old API
libraries.forEach(library => {
const {unstableModulesRequiringMainQueueSetup} = library?.config?.ios || {};
if (!unstableModulesRequiringMainQueueSetup) {
return;
}
for (const moduleName of unstableModulesRequiringMainQueueSetup) {
modulesRequiringMainQueueSetup.add(moduleName);
}
});
// New API
for (const {modules: moduleAnnotationMap} of Object.values(iosAnnotations)) {
for (const [moduleName, annotation] of Object.entries(
moduleAnnotationMap,
)) {
if (annotation.unstableRequiresMainQueueSetup) {
modulesRequiringMainQueueSetup.add(moduleName);
}
}
}
const modulesStr = Array.from(modulesRequiringMainQueueSetup)
.map(className => `@"${className}"`)
.join(',\n\t\t');
const template = fs.readFileSync(
UNSTABLE_MODULES_REQUIRING_MAIN_QUEUE_SETUP_PROVIDER_MM_TEMPLATE_PATH,
'utf8',
);
const finalMMFile = template.replace(/{modules}/, modulesStr);
fs.mkdirSync(outputDir, {recursive: true});
fs.writeFileSync(
path.join(
outputDir,
'RCTUnstableModulesRequiringMainQueueSetupProvider.mm',
),
finalMMFile,
);
const templateH = fs.readFileSync(
UNSTABLE_MODULES_REQUIRING_MAIN_QUEUE_SETUP_PROVIDER_H_TEMPLATE_PATH,
'utf8',
);
fs.writeFileSync(
path.join(outputDir, 'RCTUnstableModulesRequiringMainQueueSetupProvider.h'),
templateH,
);
}
module.exports = {
generateUnstableModulesRequiringMainQueueSetupProvider,
};

View File

@@ -0,0 +1,290 @@
/**
* 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';
/**
* This script crawls through a React Native application's dependencies and invokes the codegen
* for any libraries that require it.
* To enable codegen support, the library should include a config in the codegenConfig key
* in a package.json file.
*/
const {
generateAppDependencyProvider,
} = require('./generateAppDependencyProvider');
const {generateCustomURLHandlers} = require('./generateCustomURLHandlers');
const {generateNativeCode} = require('./generateNativeCode');
const {generatePackageSwift} = require('./generatePackageSwift');
const {generateRCTModuleProviders} = require('./generateRCTModuleProviders');
const {
generateRCTThirdPartyComponents,
} = require('./generateRCTThirdPartyComponents');
const {generateReactCodegenPodspec} = require('./generateReactCodegenPodspec');
const {generateSchemaInfos} = require('./generateSchemaInfos');
const {
generateUnstableModulesRequiringMainQueueSetupProvider,
} = require('./generateUnstableModulesRequiringMainQueueSetupProvider');
const {
buildCodegenIfNeeded,
cleanupEmptyFilesAndFolders,
codegenLog,
findCodegenEnabledLibraries,
findDisabledLibrariesByPlatform,
findReactNativeRootPath,
pkgJsonIncludesGeneratedCode,
readPkgJsonInDirectory,
readReactNativeConfig,
} = require('./utils');
const path = require('path');
/**
* This function is the entry point for the codegen. It:
* - reads the package json
* - extracts the libraries
* - setups the CLI to generate the code
* - generate the code
*
* @parameter projectRoot: the directory with the app source code, where the package.json lives.
* @parameter baseOutputPath: the base output path for the CodeGen.
* @parameter targetPlatform: the target platform. Supported values: 'android', 'ios', 'all'.
* @parameter source: the source that is invoking codegen. Supported values: 'app', 'library'.
* @throws If it can't find a config file for react-native.
* @throws If it can't find a CodeGen configuration in the file.
* @throws If it can't find a cli for the CodeGen.
*/
function execute(
projectRoot /*: string */,
targetPlatform /*: string */,
optionalBaseOutputPath /*: ?string */,
source /*: string */,
runReactNativeCodegen /*: boolean */ = true,
) {
try {
codegenLog(`Analyzing ${path.join(projectRoot, 'package.json')}`);
const supportedPlatforms = ['android', 'ios'];
if (
targetPlatform !== 'all' &&
!supportedPlatforms.includes(targetPlatform)
) {
throw new Error(
`Invalid target platform: ${targetPlatform}. Supported values are: ${supportedPlatforms.join(
', ',
)}, all`,
);
}
const pkgJson = readPkgJsonInDirectory(projectRoot);
if (runReactNativeCodegen) {
buildCodegenIfNeeded();
}
const platforms =
targetPlatform === 'all' ? supportedPlatforms : [targetPlatform];
// NOTE: We cache the external libraries search (which may not run) across platforms to not change previous behaviour
const externalLibrariesCache /*: { current?: ?Array<$FlowFixMe> } */ = {};
for (const platform of platforms) {
// NOTE: This needs to be computed per-platform since `platform` can alter the path via a `package.json:codegenConfig.outputDir[platform]` override
const baseOutputPath = computeBaseOutputPath(
projectRoot,
optionalBaseOutputPath,
pkgJson,
platform,
);
const reactNativeConfig = readReactNativeConfig(
projectRoot,
baseOutputPath,
);
const codegenEnabledLibraries = findCodegenEnabledLibraries(
pkgJson,
projectRoot,
baseOutputPath,
reactNativeConfig,
externalLibrariesCache,
);
if (codegenEnabledLibraries.length === 0) {
codegenLog('No codegen-enabled libraries found.', true);
}
const disabledLibraries = findDisabledLibrariesByPlatform(
reactNativeConfig,
platform,
);
const libraries = codegenEnabledLibraries.filter(
({name}) => !disabledLibraries.includes(name),
);
const outputPath = computeOutputPath(
projectRoot,
baseOutputPath,
pkgJson,
platform,
);
const reactCodegenOutputPath =
platform === 'android'
? outputPath
: path.join(outputPath, 'ReactCodegen');
if (runReactNativeCodegen) {
const schemaInfos = generateSchemaInfos(libraries);
generateNativeCode(
reactCodegenOutputPath,
schemaInfos.filter(schemaInfo =>
mustGenerateNativeCode(projectRoot, schemaInfo),
),
pkgJsonIncludesGeneratedCode(pkgJson),
platform,
);
}
if (source === 'app' && platform !== 'android') {
// These components are only required by apps, not by libraries and are Apple specific.
generateRCTThirdPartyComponents(libraries, reactCodegenOutputPath);
generateRCTModuleProviders(
projectRoot,
pkgJson,
libraries,
reactCodegenOutputPath,
);
generateCustomURLHandlers(libraries, reactCodegenOutputPath);
generateUnstableModulesRequiringMainQueueSetupProvider(
libraries,
reactCodegenOutputPath,
);
generateAppDependencyProvider(
path.join(outputPath, 'ReactAppDependencyProvider'),
);
generateReactCodegenPodspec(
projectRoot,
pkgJson,
reactCodegenOutputPath,
baseOutputPath,
);
generatePackageSwift(
projectRoot,
outputPath,
findReactNativeRootPath(projectRoot),
);
}
cleanupEmptyFilesAndFolders(outputPath);
}
} catch (err) {
codegenLog(err);
process.exitCode = 1;
}
codegenLog('Done.', true);
return;
}
function readOutputDirFromPkgJson(
pkgJson /*: $FlowFixMe */,
platform /*: string */,
) {
const codegenConfig = pkgJson.codegenConfig;
if (codegenConfig == null || typeof codegenConfig !== 'object') {
return null;
}
const outputDir = codegenConfig.outputDir;
if (outputDir == null) {
return null;
}
if (typeof outputDir === 'string') {
return outputDir;
}
if (typeof outputDir === 'object') {
return outputDir[platform];
}
return null;
}
function computeBaseOutputPath(
projectRoot /*: string */,
optionalBaseOutputPath /*: ?string */,
pkgJson /*: $FlowFixMe */,
platform /*: string */,
) {
if (
process.env.RCT_SCRIPT_OUTPUT_DIR != null &&
process.env.RCT_SCRIPT_OUTPUT_DIR.length > 0
) {
return process.env.RCT_SCRIPT_OUTPUT_DIR;
}
let baseOutputPath /*: string */;
if (optionalBaseOutputPath == null) {
const outputDirFromPkgJson = readOutputDirFromPkgJson(pkgJson, platform);
if (outputDirFromPkgJson != null) {
baseOutputPath = path.join(projectRoot, outputDirFromPkgJson);
} else {
baseOutputPath = projectRoot;
}
} else {
baseOutputPath = optionalBaseOutputPath;
}
return baseOutputPath;
}
function computeOutputPath(
projectRoot /*: string */,
baseOutputPath /*: string */,
pkgJson /*: $FlowFixMe */,
platform /*: string */,
) /*: string */ {
if (pkgJsonIncludesGeneratedCode(pkgJson)) {
// Don't create nested directories for libraries to make importing generated headers easier.
return baseOutputPath;
}
if (platform === 'android') {
return defaultOutputPathForAndroid(baseOutputPath);
}
if (platform === 'ios') {
return defaultOutputPathForIOS(baseOutputPath);
}
return baseOutputPath;
}
function defaultOutputPathForAndroid(baseOutputPath /*: string */) {
return path.join(
baseOutputPath,
'android',
'app',
'build',
'generated',
'source',
'codegen',
);
}
function defaultOutputPathForIOS(baseOutputPath /*: string */) {
return path.join(baseOutputPath, 'build', 'generated', 'ios');
}
function mustGenerateNativeCode(
includeLibraryPath /*: string */,
schemaInfo /*: $FlowFixMe */,
) {
// If library's 'codegenConfig' sets 'includesGeneratedCode' to 'true',
// then we assume that native code is shipped with the library,
// and we don't need to generate it.
return (
schemaInfo.library.libraryPath === includeLibraryPath ||
!schemaInfo.library.config.includesGeneratedCode
);
}
module.exports = {
execute,
};

View File

@@ -0,0 +1,502 @@
/**
* 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';
const {
CODEGEN_REPO_PATH,
CORE_LIBRARIES_WITH_OUTPUT_FOLDER,
REACT_NATIVE,
} = require('./constants');
const {execSync} = require('child_process');
const fs = require('fs');
const path = require('path');
function pkgJsonIncludesGeneratedCode(
pkgJson /*: $FlowFixMe */,
) /*: boolean */ {
return pkgJson.codegenConfig && pkgJson.codegenConfig.includesGeneratedCode;
}
const codegenLog = (text /*: string */, info /*: boolean */ = false) => {
// ANSI escape codes for colors and formatting
const reset = '\x1b[0m';
const cyan = '\x1b[36m';
const yellow = '\x1b[33m';
const bold = '\x1b[1m';
const color = info ? yellow : '';
console.log(`${cyan}${bold}[Codegen]${reset} ${color}${text}${reset}`);
};
function readPkgJsonInDirectory(dir /*: string */) /*: $FlowFixMe */ {
const pkgJsonPath = path.join(dir, 'package.json');
if (!fs.existsSync(pkgJsonPath)) {
throw new Error(`[Codegen] Error: ${pkgJsonPath} does not exist.`);
}
return JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
}
function buildCodegenIfNeeded() {
if (!fs.existsSync(CODEGEN_REPO_PATH)) {
return;
}
// Assuming we are working in the react-native repo. We might need to build the codegen.
// This will become unnecessary once we start using Babel Register for the codegen package.
const libPath = path.join(CODEGEN_REPO_PATH, 'lib');
if (fs.existsSync(libPath) && fs.readdirSync(libPath).length > 0) {
return;
}
codegenLog('Building react-native-codegen package.', true);
execSync('yarn install', {
cwd: CODEGEN_REPO_PATH,
stdio: 'inherit',
});
execSync('yarn build', {
cwd: CODEGEN_REPO_PATH,
stdio: 'inherit',
});
}
// It removes all the empty files and empty folders
// it finds, starting from `filepath`, recursively.
//
// This function is needed since, after aligning the codegen between
// iOS and Android, we have to create empty folders in advance and
// we don't know whether they will be populated up until the end of the process.
//
// @parameter filepath: the root path from which we want to remove the empty files and folders.
function cleanupEmptyFilesAndFolders(filepath /*: string */) {
const stats = fs.statSync(filepath);
if (stats.isFile() && stats.size === 0) {
fs.rmSync(filepath);
return;
} else if (stats.isFile()) {
return;
}
const dirContent = fs.readdirSync(filepath);
dirContent.forEach(contentPath =>
cleanupEmptyFilesAndFolders(path.join(filepath, contentPath)),
);
// The original folder may be filled with empty folders
// if that the case, we would also like to remove the parent.
// Hence, we need to read the folder again.
const newContent = fs.readdirSync(filepath);
if (newContent.length === 0) {
fs.rmdirSync(filepath);
return;
}
}
function readGeneratedAutolinkingOutput(
projectRoot /*: string */,
baseOutputPath /*: string */,
) /*: $FlowFixMe */ {
// NOTE: Generated by scripts/cocoapods/autolinking.rb in list_native_modules (called by use_native_modules)
// The `baseOutputPath` is based on a CLI argument and optional
const autolinkingGeneratedPath = path.resolve(
baseOutputPath,
'build/generated/autolinking/autolinking.json',
);
if (fs.existsSync(autolinkingGeneratedPath)) {
// $FlowFixMe[unsupported-syntax]
return require(autolinkingGeneratedPath);
} else {
codegenLog(
`Could not find generated autolinking output at: ${autolinkingGeneratedPath}`,
);
return null;
}
}
function readReactNativeConfig(
projectRoot /*: string */,
baseOutputPath /*: string */,
) /*: $FlowFixMe */ {
const autolinkingOutput = readGeneratedAutolinkingOutput(
projectRoot,
baseOutputPath,
);
const rnConfigFilePath = path.resolve(projectRoot, 'react-native.config.js');
if (autolinkingOutput) {
return autolinkingOutput;
} else if (fs.existsSync(rnConfigFilePath)) {
// $FlowFixMe[unsupported-syntax]
return require(rnConfigFilePath);
} else {
codegenLog(`Could not find React Native config at: ${rnConfigFilePath}`);
return {};
}
}
/**
* Finding libraries!
*/
function findCodegenEnabledLibraries(
pkgJson /*: $FlowFixMe */,
projectRoot /*: string */,
baseOutputPath /*: string */,
reactNativeConfig /*: $FlowFixMe */,
externalLibrariesCache /*: { current?: ?Array<$FlowFixMe> } */ = {},
) /*: Array<$FlowFixMe> */ {
const projectLibraries = findProjectRootLibraries(pkgJson, projectRoot);
if (pkgJsonIncludesGeneratedCode(pkgJson)) {
return projectLibraries;
} else {
const libraries = [...projectLibraries];
// If we ran autolinking, we shouldn't try to run our own "autolinking-like"
// library discovery
if (!readGeneratedAutolinkingOutput(projectRoot, baseOutputPath)) {
const externalLibraries =
externalLibrariesCache.current ??
(externalLibrariesCache.current = findExternalLibraries(
pkgJson,
projectRoot,
));
libraries.push(...externalLibraries);
}
libraries.push(
...findLibrariesFromReactNativeConfig(projectRoot, reactNativeConfig),
);
return libraries;
}
}
function findProjectRootLibraries(
pkgJson /*: $FlowFixMe */,
projectRoot /*: string */,
) /*: Array<$FlowFixMe> */ {
codegenLog('Searching for codegen-enabled libraries in the app.', true);
if (pkgJson.codegenConfig == null) {
codegenLog(
'The "codegenConfig" field is not defined in package.json. Assuming there is nothing to generate at the app level.',
true,
);
return [];
}
if (typeof pkgJson.codegenConfig !== 'object') {
throw new Error('The "codegenConfig" field must be an Object.');
}
return extractLibrariesFromJSON(pkgJson, projectRoot);
}
function findLibrariesFromReactNativeConfig(
projectRoot /*: string */,
rnConfig /*: $FlowFixMe */,
) /*: Array<$FlowFixMe> */ {
codegenLog(
`Searching for codegen-enabled libraries in react-native.config.js`,
true,
);
if (!rnConfig.dependencies) {
return [];
}
return Object.keys(rnConfig.dependencies).flatMap(name => {
const dependencyConfig = rnConfig.dependencies[name];
if (!dependencyConfig.root) {
return [];
}
const codegenConfigFileDir = path.resolve(
projectRoot,
dependencyConfig.root,
);
let configFile;
try {
configFile = readPkgJsonInDirectory(codegenConfigFileDir);
} catch {
return [];
}
return extractLibrariesFromJSON(configFile, codegenConfigFileDir);
});
}
function findExternalLibraries(
pkgJson /*: $FlowFixMe */,
projectRoot /*: string */,
) /*: Array<$FlowFixMe> */ {
const dependencies = {
...pkgJson.dependencies,
...pkgJson.devDependencies,
...pkgJson.peerDependencies,
};
// Determine which of these are codegen-enabled libraries
codegenLog(
'Searching for codegen-enabled libraries in the project dependencies.',
true,
);
// Handle third-party libraries
return Object.keys(dependencies).flatMap(dependency => {
let configFilePath = '';
try {
configFilePath = require.resolve(path.join(dependency, 'package.json'), {
paths: [projectRoot],
});
} catch (e) {
// require.resolve fails if the dependency is a local node module.
if (
// require.resolve fails if the `./package.json` subpath is not explicitly defined in the library's `exports` field in its package.json
'code' in e &&
e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED'
) {
// find the closest library's package.json with the search paths
// $FlowFixMe[prop-missing]
const paths /*: Array<string>*/ = require.main.paths;
for (const nodeModulesPath of paths) {
const packageJsonFilePath = path.join(
nodeModulesPath,
dependency,
'package.json',
);
if (fs.existsSync(packageJsonFilePath)) {
configFilePath = packageJsonFilePath;
break;
}
}
} else if (
// require.resolve fails if the dependency is a local node module.
dependencies[dependency].startsWith('.') || // handles relative paths
dependencies[dependency].startsWith('/') // handles absolute paths
) {
configFilePath = path.join(
projectRoot,
pkgJson.dependencies[dependency],
'package.json',
);
}
if (!configFilePath) {
return [];
}
}
const configFile = JSON.parse(fs.readFileSync(configFilePath, 'utf8'));
const codegenConfigFileDir = path.dirname(configFilePath);
return extractLibrariesFromJSON(configFile, codegenConfigFileDir);
});
}
function extractLibrariesFromJSON(
configFile /*: $FlowFixMe */,
dependencyPath /*: string */,
) /*: Array<$FlowFixMe> */ {
if (configFile.codegenConfig == null) {
return [];
}
codegenLog(`Found ${configFile.name}`);
if (configFile.codegenConfig.libraries == null) {
const config = configFile.codegenConfig;
return [
{
name: configFile.name,
config,
libraryPath: dependencyPath,
},
];
} else {
printDeprecationWarningIfNeeded(configFile.name);
return extractLibrariesFromConfigurationArray(configFile, dependencyPath);
}
}
function printDeprecationWarningIfNeeded(dependency /*: string */) {
if (dependency === REACT_NATIVE) {
return;
}
codegenLog(`CodegenConfig Deprecated Setup for ${dependency}.
The configuration file still contains the codegen in the libraries array.
If possible, replace it with a single object.
`);
codegenLog(`BEFORE:
{
// ...
"codegenConfig": {
"libraries": [
{
"name": "libName1",
"type": "all|components|modules",
"jsSrcsRoot": "libName1/js"
},
{
"name": "libName2",
"type": "all|components|modules",
"jsSrcsRoot": "libName2/src"
}
]
}
}
AFTER:
{
"codegenConfig": {
"name": "libraries",
"type": "all",
"jsSrcsRoot": "."
}
}
`);
}
function extractLibrariesFromConfigurationArray(
configFile /*: $FlowFixMe */,
dependencyPath /*: string */,
) {
return configFile.codegenConfig.libraries.map(config => {
return {
name: config.name,
config,
libraryPath: dependencyPath,
};
});
}
function isReactNativeCoreLibrary(libraryName /*: string */) /*: boolean */ {
return libraryName in CORE_LIBRARIES_WITH_OUTPUT_FOLDER;
}
/**
* Returns a map of this shape:
* {
* "libraryName": {
* "library": { ... }
* "modules": {
* "moduleName": {
* "conformsToProtocols": [ "protocol1", "protocol2" ],
* "className": "RCTFooModuler",
* }
* },
* "components": {
* "componentName": {
* "className": "RCTFooComponent",
* }
* }
* }
* }
*
* Validates that modules are defined in at most one library.
* Validates that components are defined in at most one library.
*/
function parseiOSAnnotations(
libraries /*: $ReadOnlyArray<$FlowFixMe> */,
) /*: {[string]: $FlowFixMe} */ {
const mLibraryMap /*: {[string]: $FlowFixMe} */ = {};
const cLibraryMap /*: {[string]: $FlowFixMe} */ = {};
const map = {};
for (const library of libraries) {
const iosConfig = library?.config?.ios;
if (!iosConfig) {
continue;
}
const libraryName = getLibraryName(library);
map[libraryName] = map[libraryName] || {
library,
modules: {},
components: {},
};
const {modules, components} = iosConfig;
if (modules) {
for (const [moduleName, annotation] of Object.entries(modules)) {
mLibraryMap[moduleName] = mLibraryMap[moduleName] || new Set();
mLibraryMap[moduleName].add(libraryName);
map[libraryName].modules[moduleName] = {...annotation};
}
}
if (components) {
for (const [moduleName, annotation] of Object.entries(components)) {
cLibraryMap[moduleName] = cLibraryMap[moduleName] || new Set();
cLibraryMap[moduleName].add(libraryName);
map[libraryName].components[moduleName] = {...annotation};
}
}
}
const moduleConflicts = Object.entries(mLibraryMap)
.filter(([_, libraryNames]) => libraryNames.size > 1)
.map(([moduleName, libraryNames]) => {
const libraryNamesString = Array.from(libraryNames).join(', ');
return ` Module { "${moduleName}" } => Libraries{ ${libraryNamesString} }\n`;
});
const componentConflicts = Object.entries(cLibraryMap)
.filter(([_, libraryNames]) => libraryNames.size > 1)
.map(([moduleName, libraryNames]) => {
const libraryNamesString = Array.from(libraryNames).join(', ');
return ` Component { "${moduleName}" } => Libraries{ ${libraryNamesString} }\n`;
});
if (moduleConflicts.length > 0 || componentConflicts.length > 0) {
throw new Error(
'Some components or modules are declared in more than one libraries: \n' +
[...moduleConflicts, ...componentConflicts].join('\n'),
);
}
return map;
}
function getLibraryName(library /*: $FlowFixMe */) {
return JSON.parse(
fs.readFileSync(path.join(library.libraryPath, 'package.json'), 'utf8'),
).name;
}
/**
* Finds all disabled libraries by platform based the react native config.
*
* This is needed when selectively disabling libraries in react-native.config.js since codegen should exclude those libraries as well.
*/
function findDisabledLibrariesByPlatform(
reactNativeConfig /*: $FlowFixMe */,
platform /*: string */,
) /*: Array<$FlowFixMe> */ {
const dependencies = reactNativeConfig.dependencies ?? {};
return Object.keys(dependencies).filter(
dependency => dependencies[dependency].platforms?.[platform] === null,
);
}
function findReactNativeRootPath(projectRoot /* : string */) /* : string */ {
const reactNativePackageJsonPath = require.resolve(
path.join('react-native', 'package.json'),
{
paths: [projectRoot],
},
);
return path.dirname(reactNativePackageJsonPath);
}
module.exports = {
buildCodegenIfNeeded,
pkgJsonIncludesGeneratedCode,
codegenLog,
readPkgJsonInDirectory,
isReactNativeCoreLibrary,
cleanupEmptyFilesAndFolders,
findCodegenEnabledLibraries,
findProjectRootLibraries,
extractLibrariesFromJSON,
parseiOSAnnotations,
readReactNativeConfig,
findDisabledLibrariesByPlatform,
findReactNativeRootPath,
};

View File

@@ -0,0 +1,137 @@
/**
* 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';
const utils = require('./codegen-utils');
const fs = require('fs');
const path = require('path');
const GENERATORS /*: {[string]: {[string]: $ReadOnlyArray<string>}} */ = {
all: {
android: ['componentsAndroid', 'modulesAndroid', 'modulesCxx'],
ios: ['componentsIOS', 'modulesIOS', 'modulesCxx'],
},
components: {
android: ['componentsAndroid'],
ios: ['componentsIOS'],
},
modules: {
android: ['modulesAndroid', 'modulesCxx'],
ios: ['modulesIOS', 'modulesCxx'],
},
};
function createOutputDirectoryIfNeeded(
outputDirectory /*: string */,
libraryName /*: string */,
) {
if (!outputDirectory) {
// $FlowFixMe[reassign-const]
outputDirectory = path.resolve(__dirname, '..', 'Libraries', libraryName);
}
fs.mkdirSync(outputDirectory, {recursive: true});
}
/**
* This function read a JSON schema from a path and parses it.
* It throws if the schema don't exists or it can't be parsed.
*
* @parameter schemaPath: the path to the schema
* @return a valid schema
* @throw an Error if the schema doesn't exists in a given path or if it can't be parsed.
*/
function readAndParseSchema(schemaPath /*: string */) {
const schemaText = fs.readFileSync(schemaPath, 'utf-8');
if (schemaText == null) {
throw new Error(`Can't find schema at ${schemaPath}`);
}
try {
return JSON.parse(schemaText);
} catch (err) {
throw new Error(`Can't parse schema to JSON. ${schemaPath}`);
}
}
function validateLibraryType(libraryType /*: string */) {
if (GENERATORS[libraryType] == null) {
throw new Error(`Invalid library type. ${libraryType}`);
}
}
function generateSpecFromInMemorySchema(
platform /*: string */,
schema /*: string */,
outputDirectory /*: string */,
libraryName /*: string */,
packageName /*: string */,
libraryType /*: string */,
useLocalIncludePaths /*: boolean */,
) {
validateLibraryType(libraryType);
createOutputDirectoryIfNeeded(outputDirectory, libraryName);
const includeGetDebugPropsImplementation =
libraryName.includes('FBReactNativeSpec'); //only generate getDebugString for React Native Core Components
utils.getCodegen().generate(
{
libraryName,
schema,
outputDirectory,
packageName,
assumeNonnull: platform === 'ios',
useLocalIncludePaths,
includeGetDebugPropsImplementation,
},
{
generators: GENERATORS[libraryType][platform],
},
);
if (platform === 'android') {
// Move all components C++ files to a structured jni folder for now.
// Note: this should've been done by RNCodegen's generators, but:
// * the generators don't support platform option yet
// * this subdir structure is Android-only, not applicable to iOS
const files = fs.readdirSync(outputDirectory);
const jniOutputDirectory = `${outputDirectory}/jni/react/renderer/components/${libraryName}`;
fs.mkdirSync(jniOutputDirectory, {recursive: true});
files
.filter(f => f.endsWith('.h') || f.endsWith('.cpp'))
.forEach(f => {
fs.renameSync(`${outputDirectory}/${f}`, `${jniOutputDirectory}/${f}`);
});
}
}
function generateSpec(
platform /*: string */,
schemaPath /*: string */,
outputDirectory /*: string */,
libraryName /*: string */,
packageName /*: string */,
libraryType /*: string */,
) {
// $FlowFixMe[incompatible-type]
generateSpecFromInMemorySchema(
platform,
readAndParseSchema(schemaPath),
outputDirectory,
libraryName,
packageName,
libraryType,
);
}
module.exports = {
execute: generateSpec,
generateSpecFromInMemorySchema,
};

View File

@@ -0,0 +1,59 @@
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "React-GeneratedCode",
platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "ReactCodegen",
targets: ["ReactCodegen"]),
.library(
name: "ReactAppDependencyProvider",
targets: ["ReactAppDependencyProvider"]),
],
dependencies: [
.package(name: "React", path: "{reactNativePath}")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "ReactCodegen",
dependencies: ["React"],
path: "ReactCodegen",
exclude: ["ReactCodegen.podspec"],
publicHeadersPath: ".",
cSettings: [
.headerSearchPath("headers")
],
cxxSettings: [
.headerSearchPath("headers"),
.unsafeFlags(["-std=c++20"]),
],
linkerSettings: [
.linkedFramework("Foundation")
]
),
.target(
name: "ReactAppDependencyProvider",
dependencies: ["ReactCodegen"],
path: "ReactAppDependencyProvider",
exclude: ["ReactAppDependencyProvider.podspec"],
publicHeadersPath: ".",
cSettings: [
.headerSearchPath("headers"),
],
cxxSettings: [
.headerSearchPath("headers"),
.unsafeFlags(["-std=c++20"]),
],
linkerSettings: [
.linkedFramework("Foundation")
]
)
]
)

View File

@@ -0,0 +1,25 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#if __has_include(<React-RCTAppDelegate/RCTDependencyProvider.h>)
#import <React-RCTAppDelegate/RCTDependencyProvider.h>
#elif __has_include(<React_RCTAppDelegate/RCTDependencyProvider.h>)
#import <React_RCTAppDelegate/RCTDependencyProvider.h>
#else
#import "RCTDependencyProvider.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface RCTAppDependencyProvider : NSObject <RCTDependencyProvider>
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,40 @@
/*
* 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.
*/
#import "RCTAppDependencyProvider.h"
#import <ReactCodegen/RCTModulesConformingToProtocolsProvider.h>
#import <ReactCodegen/RCTThirdPartyComponentsProvider.h>
#import <ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.h>
#import <ReactCodegen/RCTModuleProviders.h>
@implementation RCTAppDependencyProvider
- (nonnull NSArray<NSString *> *)URLRequestHandlerClassNames {
return RCTModulesConformingToProtocolsProvider.URLRequestHandlerClassNames;
}
- (nonnull NSArray<NSString *> *)imageDataDecoderClassNames {
return RCTModulesConformingToProtocolsProvider.imageDataDecoderClassNames;
}
- (nonnull NSArray<NSString *> *)imageURLLoaderClassNames {
return RCTModulesConformingToProtocolsProvider.imageURLLoaderClassNames;
}
- (nonnull NSArray<NSString *> *)unstableModulesRequiringMainQueueSetup {
return RCTUnstableModulesRequiringMainQueueSetupProvider.modules;
}
- (nonnull NSDictionary<NSString *,Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents {
return RCTThirdPartyComponentsProvider.thirdPartyFabricComponents;
}
- (nonnull NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders {
return RCTModuleProviders.moduleProviders;
}
@end

View File

@@ -0,0 +1,16 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@protocol RCTModuleProvider;
@interface RCTModuleProviders: NSObject
+ (NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders;
@end

View File

@@ -0,0 +1,51 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import "RCTModuleProviders.h"
#import <ReactCommon/RCTTurboModule.h>
#import <React/RCTLog.h>
@implementation RCTModuleProviders
+ (NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders
{
static NSDictionary<NSString *, id<RCTModuleProvider>> *providers = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDictionary<NSString *, NSString *> * moduleMapping = @{
{moduleMapping}
};
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:moduleMapping.count];
for (NSString *key in moduleMapping) {
NSString * moduleProviderName = moduleMapping[key];
Class klass = NSClassFromString(moduleProviderName);
if (!klass) {
RCTLogError(@"Module provider %@ cannot be found in the runtime", moduleProviderName);
continue;
}
id instance = [klass new];
if (![instance respondsToSelector:@selector(getTurboModule:)]) {
RCTLogError(@"Module provider %@ does not conform to RCTModuleProvider", moduleProviderName);
continue;
}
[dict setObject:instance forKey:key];
}
providers = dict;
});
return providers;
}
@end

View File

@@ -0,0 +1,18 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@interface RCTModulesConformingToProtocolsProvider: NSObject
+(NSArray<NSString *> *)imageURLLoaderClassNames;
+(NSArray<NSString *> *)imageDataDecoderClassNames;
+(NSArray<NSString *> *)URLRequestHandlerClassNames;
@end

View File

@@ -0,0 +1,54 @@
/*
* 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.
*/
#import "RCTModulesConformingToProtocolsProvider.h"
@implementation RCTModulesConformingToProtocolsProvider
+(NSArray<NSString *> *)imageURLLoaderClassNames
{
static NSArray<NSString *> *classNames = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classNames = @[
{imageURLLoaderClassNames}
];
});
return classNames;
}
+(NSArray<NSString *> *)imageDataDecoderClassNames
{
static NSArray<NSString *> *classNames = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classNames = @[
{imageDataDecoderClassNames}
];
});
return classNames;
}
+(NSArray<NSString *> *)URLRequestHandlerClassNames
{
static NSArray<NSString *> *classNames = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classNames = @[
{requestHandlersClassNames}
];
});
return classNames;
}
@end

View File

@@ -0,0 +1,16 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@protocol RCTComponentViewProtocol;
@interface RCTThirdPartyComponentsProvider: NSObject
+ (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents;
@end

View File

@@ -0,0 +1,30 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import "RCTThirdPartyComponentsProvider.h"
#import <React/RCTComponentViewProtocol.h>
@implementation RCTThirdPartyComponentsProvider
+ (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
{
static NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *thirdPartyComponents = nil;
static dispatch_once_t nativeComponentsToken;
dispatch_once(&nativeComponentsToken, ^{
thirdPartyComponents = @{
{thirdPartyComponentsMapping}
};
});
return thirdPartyComponents;
}
@end

View File

@@ -0,0 +1,14 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@interface RCTUnstableModulesRequiringMainQueueSetupProvider: NSObject
+(NSArray<NSString *> *)modules;
@end

View File

@@ -0,0 +1,19 @@
/*
* 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.
*/
#import "RCTUnstableModulesRequiringMainQueueSetupProvider.h"
@implementation RCTUnstableModulesRequiringMainQueueSetupProvider
+(NSArray<NSString *> *)modules
{
return @[
{modules}
];
}
@end

View File

@@ -0,0 +1,34 @@
# 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.
version = "{react-native-version}"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |s|
s.name = "ReactAppDependencyProvider"
s.version = version
s.summary = "The third party dependency provider for the app"
s.homepage = "https://reactnative.dev/"
s.documentation_url = "https://reactnative.dev/"
s.license = "{react-native-licence}"
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = "**/RCTAppDependencyProvider.{h,mm}"
# This guard prevent to install the dependencies when we run `pod install` in the old architecture.
s.pod_target_xcconfig = {
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"DEFINES_MODULE" => "YES"
}
s.dependency "ReactCodegen"
end

View File

@@ -0,0 +1,97 @@
# 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.
version = "{react-native-version}"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
use_frameworks = ENV['USE_FRAMEWORKS'] != nil
folly_compiler_flags = Helpers::Constants.folly_config[:compiler_flags]
boost_compiler_flags = Helpers::Constants.boost_config[:compiler_flags]
header_search_paths = []
framework_search_paths = []
header_search_paths = [
"\"$(PODS_ROOT)/ReactNativeDependencies\"",
"\"${PODS_ROOT}/Headers/Public/ReactCodegen/react/renderer/components\"",
"\"$(PODS_ROOT)/Headers/Private/React-Fabric\"",
"\"$(PODS_ROOT)/Headers/Private/React-RCTFabric\"",
"\"$(PODS_ROOT)/Headers/Private/Yoga\"",
"\"$(PODS_TARGET_SRCROOT)\"",
]
if use_frameworks
ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-Fabric", "React_Fabric", ["react/renderer/components/view/platform/cxx"])
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-FabricImage", "React_FabricImage", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-graphics", "React_graphics", ["react/renderer/graphics/platform/ios"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "ReactCommon", "ReactCommon", ["react/nativemodule/core"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-runtimeexecutor", "React_runtimeexecutor", ["platform/ios"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-NativeModulesApple", "React_NativeModulesApple", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-RCTFabric", "RCTFabric", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-debug", "React_debug", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-rendererdebug", "React_rendererdebug", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-utils", "React_utils", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-featureflags", "React_featureflags", []))
.each { |search_path|
header_search_paths << "\"#{search_path}\""
}
end
Pod::Spec.new do |s|
s.name = "ReactCodegen"
s.version = version
s.summary = 'Temp pod for generated files for React Native'
s.homepage = 'https://facebook.com/'
s.license = 'Unlicense'
s.authors = 'Facebook'
s.compiler_flags = "#{folly_compiler_flags} #{boost_compiler_flags} -Wno-nullability-completeness -std=c++20"
s.source = { :git => '' }
s.header_mappings_dir = './'
s.platforms = min_supported_versions
s.source_files = "**/*.{h,mm,cpp}"
s.exclude_files = "RCTAppDependencyProvider.{h,mm}" # these files are generated in the same codegen path but needs to belong to a different pod
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => header_search_paths.join(' '),
"FRAMEWORK_SEARCH_PATHS" => framework_search_paths,
"OTHER_CPLUSPLUSFLAGS" => "$(inherited) #{folly_compiler_flags} #{boost_compiler_flags}"
}
s.dependency "React-jsiexecutor"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "React-Core"
s.dependency "React-jsi"
s.dependency "ReactCommon/turbomodule/bridging"
s.dependency "ReactCommon/turbomodule/core"
s.dependency "React-NativeModulesApple"
s.dependency 'React-graphics'
s.dependency 'React-rendererdebug'
s.dependency 'React-Fabric'
s.dependency 'React-FabricImage'
s.dependency 'React-debug'
s.dependency 'React-utils'
s.dependency 'React-featureflags'
s.dependency 'React-RCTAppDelegate'
depend_on_js_engine(s)
add_rn_third_party_dependencies(s)
add_rncore_dependency(s)
s.script_phases = {
'name' => 'Generate Specs',
'execution_position' => :before_compile,
'input_files' => {input-files},
'show_env_vars_in_log' => true,
'output_files' => ["${DERIVED_FILE_DIR}/react-codegen.log"],
'script': {codegen-script}
}
end

67
node_modules/react-native/scripts/compose-source-maps.js generated vendored Executable file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env node
/**
* 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';
const fs = require('fs');
const {composeSourceMaps} = require('metro-source-map');
const argv = process.argv.slice(2);
let outputPath /*: ?string */;
for (let i = 0; i < argv.length; ) {
if (argv[i] === '-o') {
outputPath = argv[i + 1];
argv.splice(i, 2);
continue;
}
++i;
}
if (!argv.length) {
process.stderr.write(
'Usage: node compose-source-maps.js <packager_sourcemap> <compiler_sourcemap> [-o output_file]\n',
);
process.exitCode = -1;
} else {
const [packagerSourcemapPath, compilerSourcemapPath] = argv.splice(0, 2);
const packagerSourcemap = JSON.parse(
fs.readFileSync(packagerSourcemapPath, 'utf8'),
);
const compilerSourcemap = JSON.parse(
fs.readFileSync(compilerSourcemapPath, 'utf8'),
);
if (
packagerSourcemap.x_facebook_offsets != null ||
compilerSourcemap.x_facebook_offsets != null
) {
throw new Error(
'Random Access Bundle (RAM) format is not supported by this tool; ' +
'it cannot process the `x_facebook_offsets` field provided ' +
'in the base and/or target source map(s)',
);
}
if (compilerSourcemap.x_facebook_segments != null) {
throw new Error(
'This tool cannot process the `x_facebook_segments` field provided ' +
'in the target source map.',
);
}
const composedMapJSON = JSON.stringify(
composeSourceMaps([packagerSourcemap, compilerSourcemap]),
);
if (outputPath != null) {
fs.writeFileSync(outputPath, composedMapJSON, 'utf8');
} else {
process.stdout.write(composedMapJSON);
}
}

View File

@@ -0,0 +1,83 @@
#!/bin/bash
# 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.
set -e
# WHY WE NEED THIS:
# This script is used to find a valid instance of `node` installed in the machine.
# This script is sourced by other scripts to get access to node.
# Specifically, it is used by the `react-native-xcode.sh` script, invoked by a
# post-build phase in Xcode, to build the js files required by React Native.
#
# DEPRECATION NOTE:
# React Native should not make assumptions on your current node environment.
# This file is deprecated and will be removed in a future release in favor of something
# node-agnostic and configurable by the developers.
# remove global prefix if it's already set
# the running shell process will choose a node binary and a global package directory breaks version managers
unset PREFIX
# Support Homebrew on Apple Silicon
HOMEBREW_APPLE_SILICON_BIN=/opt/homebrew/bin
if [[ -d $HOMEBREW_APPLE_SILICON_BIN && ! $PATH =~ $HOMEBREW_APPLE_SILICON_BIN ]]; then
export PATH="$HOMEBREW_APPLE_SILICON_BIN:$PATH"
fi
# Define NVM_DIR and source the nvm.sh setup script
[ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm"
# Source nvm with '--no-use' and then `nvm use` to respect .nvmrc
# See: https://github.com/nvm-sh/nvm/issues/2053
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
# shellcheck source=/dev/null
. "$HOME/.nvm/nvm.sh" --no-use
nvm use 2> /dev/null || nvm use default
elif [[ -x "$(command -v brew)" && -s "$(brew --prefix nvm)/nvm.sh" ]]; then
# shellcheck source=/dev/null
. "$(brew --prefix nvm)/nvm.sh" --no-use
nvm use 2> /dev/null || nvm use default
fi
# Set up the nodenv node version manager if present
if [[ -x "$HOME/.nodenv/bin/nodenv" ]]; then
eval "$("$HOME/.nodenv/bin/nodenv" init -)"
elif [[ -x "$(command -v brew)" && -x "$(brew --prefix nodenv)/bin/nodenv" ]]; then
eval "$("$(brew --prefix nodenv)/bin/nodenv" init -)"
fi
# Set up the ndenv of anyenv if preset
if [[ ! -x node && -d ${HOME}/.anyenv/bin ]]; then
export PATH=${HOME}/.anyenv/bin:${PATH}
if [[ "$(anyenv envs | grep -c ndenv )" -eq 1 ]]; then
eval "$(anyenv init -)"
fi
fi
# Set up asdf-vm if present
if [[ -f "$ASDF_DIR/asdf.sh" ]]; then
# shellcheck source=/dev/null
. "$ASDF_DIR/asdf.sh"
elif [[ -f "$HOME/.asdf/asdf.sh" ]]; then
# shellcheck source=/dev/null
. "$HOME/.asdf/asdf.sh"
elif [[ -x "$(command -v brew)" && -f "$(brew --prefix asdf)/asdf.sh" ]]; then
# shellcheck source=/dev/null
. "$(brew --prefix asdf)/asdf.sh"
fi
# Set up volta if present
if [[ -x "$HOME/.volta/bin/node" ]]; then
export VOLTA_HOME="$HOME/.volta"
export PATH="$VOLTA_HOME/bin:$PATH"
fi
# Set up the fnm node version manager if present
if [[ -x "$HOME/.fnm/fnm" ]]; then
eval "$("$HOME/.fnm/fnm" env)"
elif [[ -x "$(command -v brew)" && -x "$(brew --prefix fnm)/bin/fnm" ]]; then
eval "$("$(brew --prefix fnm)/bin/fnm" env)"
fi

View File

@@ -0,0 +1,38 @@
/**
* 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';
const executor = require('./codegen/generate-artifacts-executor');
const yargs = require('yargs');
const argv = yargs
.option('p', {
alias: 'path',
description: 'Path to the React Native project root.',
})
.option('t', {
alias: 'targetPlatform',
description: 'Target platform. Supported values: "android", "ios", "all".',
})
.option('o', {
alias: 'outputPath',
description: 'Path where generated artifacts will be output to.',
})
.option('s', {
alias: 'source',
description: 'Whether the script is invoked from an `app` or a `library`',
default: 'app',
})
.usage('Usage: $0 -p [path to app] -t [target platform] -o [output path]')
.demandOption(['p', 't']).argv;
// $FlowFixMe[prop-missing]
executor.execute(argv.path, argv.targetPlatform, argv.outputPath, argv.source);

View File

@@ -0,0 +1,95 @@
/**
* 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';
const utils = require('./codegen/codegen-utils');
const fs = require('fs');
const yargs = require('yargs');
const argv = yargs
.option('p', {
alias: 'platform',
describe: 'Platform to generate native code artifacts for.',
})
.option('s', {
alias: 'schemaListPath',
describe: 'The path to the schema list file.',
})
.option('o', {
alias: 'outputDir',
describe:
'Path to directory where native code source files should be saved.',
})
.usage('Usage: $0 <args>')
.demandOption(
['platform', 'schemaListPath', 'outputDir'],
'Please provide platform, schema path, and output directory.',
).argv;
const GENERATORS /*: {[string]: $ReadOnlyArray<string>} */ = {
android: [],
ios: ['providerIOS'],
};
function generateProvider(
platform /*: string */,
schemaListPath /*: string */,
outputDirectory /*: string */,
) {
const schemaListText = fs.readFileSync(schemaListPath, 'utf-8');
if (schemaListText == null) {
throw new Error(`Can't find schema list file at ${schemaListPath}`);
}
if (!outputDirectory) {
throw new Error('outputDir is required');
}
fs.mkdirSync(outputDirectory, {recursive: true});
let schemaPaths;
try {
schemaPaths = JSON.parse(schemaListText);
} catch (err) {
throw new Error(`Can't parse schema to JSON. ${schemaListPath}`);
}
const schemas /*: {[string]: $FlowFixMe} */ = {};
try {
for (const libraryName of Object.keys(schemaPaths)) {
const tmpSchemaText = fs.readFileSync(schemaPaths[libraryName], 'utf-8');
schemas[libraryName] = JSON.parse(tmpSchemaText);
}
} catch (err) {
throw new Error(`Failed to read schema file. ${err.message}`);
}
if (GENERATORS[platform] == null) {
throw new Error(`Invalid platform type. ${platform}`);
}
utils.getCodegen().generateFromSchemas(
{
schemas,
outputDirectory,
},
{
generators: GENERATORS[platform],
},
);
}
function main() {
// $FlowFixMe[prop-missing]
generateProvider(argv.platform, argv.schemaListPath, argv.outputDir);
}
main();

View File

@@ -0,0 +1,68 @@
/**
* 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';
const executor = require('./codegen/generate-specs-cli-executor');
const yargs = require('yargs');
const argv = yargs
.option('p', {
alias: 'platform',
describe: 'Platform to generate native code artifacts for.',
})
.option('s', {
alias: 'schemaPath',
describe: 'The path to the schema file.',
})
.option('o', {
alias: 'outputDir',
describe:
'Path to the root directory where native code source files should be saved.',
})
.option('n', {
alias: 'libraryName',
describe: 'Name of specs library.',
default: 'FBReactNativeSpec',
})
.option('j', {
alias: 'javaPackageName',
describe: 'Name of Java package.',
default: 'com.facebook.fbreact.specs',
})
.option('t', {
alias: 'libraryType',
describe: 'all, components, or modules.',
default: 'all',
})
.usage('Usage: $0 <args>')
.demandOption(
['platform', 'schemaPath', 'outputDir'],
'Please provide platform, schema path, and output directory.',
).argv;
function main() {
executor.execute(
// $FlowFixMe[prop-missing]
argv.platform,
// $FlowFixMe[prop-missing]
argv.schemaPath,
// $FlowFixMe[prop-missing]
argv.outputDir,
// $FlowFixMe[prop-missing]
argv.libraryName,
// $FlowFixMe[prop-missing]
argv.javaPackageName,
// $FlowFixMe[prop-missing]
argv.libraryType,
);
}
main();

View File

@@ -0,0 +1,111 @@
/**
* 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';
const fs = require('fs');
// $FlowFixMe[untyped-import]
const inquirer = require('inquirer');
const path = require('path');
/*::
type BuildType = 'dry-run' | 'release' | 'nightly';
*/
const SDKS_DIR = path.normalize(path.join(__dirname, '..', '..', 'sdks'));
const HERMES_TAG_FILE_PATH = path.join(SDKS_DIR, '.hermesversion');
const HERMES_V1_TAG_FILE_PATH = path.join(SDKS_DIR, '.hermesv1version');
function readHermesTag() /*: string */ {
if (fs.existsSync(HERMES_TAG_FILE_PATH)) {
const data = fs
.readFileSync(HERMES_TAG_FILE_PATH, {
encoding: 'utf8',
flag: 'r',
})
.trim();
if (data.length > 0) {
return data;
} else {
throw new Error('[Hermes] .hermesversion file is empty.');
}
}
throw new Error('[Hermes] .hermesversion does not exist.');
}
function readHermesV1Tag() /*: string */ {
if (fs.existsSync(HERMES_V1_TAG_FILE_PATH)) {
const data = fs
.readFileSync(HERMES_V1_TAG_FILE_PATH, {
encoding: 'utf8',
flag: 'r',
})
.trim();
if (data.length > 0) {
return data;
} else {
throw new Error('[Hermes] .hermesv1version file is empty.');
}
}
throw new Error('[Hermes] .hermesv1version does not exist.');
}
async function updateHermesTag(
tagFile /*: string */,
hermesTag /*: string */,
prompt /*: string */,
) {
if (!fs.existsSync(tagFile)) {
fs.writeFileSync(tagFile, hermesTag.trim());
} else {
const previousHermesTag = fs.readFileSync(tagFile, {
encoding: 'utf8',
flag: 'r',
});
if (previousHermesTag.trim() !== hermesTag.trim()) {
const {confirmHermesTag} = await inquirer.prompt({
type: 'confirm',
name: 'confirmHermesTag',
message: `Do you want to use updtate release tag for ${prompt} from "${previousHermesTag}" to "${hermesTag}"?`,
});
if (confirmHermesTag) {
fs.writeFileSync(tagFile, hermesTag.trim());
} else {
console.log(`[${prompt}] .hermesversion file is unchanged.`);
}
} else {
console.log(`[${prompt}] .hermesversion file is unchanged.`);
}
}
}
async function setHermesTag(
hermesTag /*: string */,
hermesV1Tag /*: string */,
) {
if (!fs.existsSync(SDKS_DIR)) {
fs.mkdirSync(SDKS_DIR, {recursive: true});
}
await updateHermesTag(HERMES_TAG_FILE_PATH, hermesTag, 'Hermes');
await updateHermesTag(HERMES_V1_TAG_FILE_PATH, hermesV1Tag, 'Hermes V1');
}
module.exports = {
readHermesTag,
readHermesV1Tag,
setHermesTag,
};

99
node_modules/react-native/scripts/ios-configure-glog.sh generated vendored Executable file
View File

@@ -0,0 +1,99 @@
#!/bin/bash
# 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.
set -e
PLATFORM_NAME="${PLATFORM_NAME:-iphoneos}"
CURRENT_ARCH="${CURRENT_ARCH}"
if [ -z "$CURRENT_ARCH" ] || [ "$CURRENT_ARCH" == "undefined_arch" ]; then
# Xcode 10 beta sets CURRENT_ARCH to "undefined_arch", this leads to incorrect linker arg.
# it's better to rely on platform name as fallback because architecture differs between simulator and device
if [[ "$PLATFORM_NAME" == *"simulator"* ]]; then
CURRENT_ARCH="x86_64"
else
CURRENT_ARCH="arm64"
fi
fi
# @lint-ignore-every TXT2 Tab Literal
if [ "$CURRENT_ARCH" == "arm64" ]; then
cat <<\EOF >>fix_glog_0.3.5_apple_silicon.patch
diff --git a/config.sub b/config.sub
index 1761d8b..43fa2e8 100755
--- a/config.sub
+++ b/config.sub
@@ -1096,6 +1096,9 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
+ arm64-*)
+ basic_machine=$(echo $basic_machine | sed 's/arm64/aarch64/')
+ ;;
none)
basic_machine=none-none
os=-none
EOF
patch -p1 config.sub fix_glog_0.3.5_apple_silicon.patch
fi
XCRUN="$(which xcrun || true)"
if [ -n "$XCRUN" ]; then
export CC="$(xcrun -find -sdk $PLATFORM_NAME cc) -arch $CURRENT_ARCH -isysroot $(xcrun -sdk $PLATFORM_NAME --show-sdk-path)"
export CXX="$CC"
else
export CC="$CC:-$(which gcc)"
export CXX="$CXX:-$(which g++ || true)"
fi
export CXX="$CXX:-$CC"
# Remove automake symlink if it exists
if [ -h "test-driver" ]; then
rm test-driver
fi
# Manually disable gflags include to fix issue https://github.com/facebook/react-native/issues/28446
sed -i.bak -e 's/\@ac_cv_have_libgflags\@/0/' src/glog/logging.h.in && rm src/glog/logging.h.in.bak
sed -i.bak -e 's/HAVE_LIB_GFLAGS/HAVE_LIB_GFLAGS_DISABLED/' src/config.h.in && rm src/config.h.in.bak
./configure --host arm-apple-darwin || true
cat << EOF >> src/config.h
/* Add in so we have Apple Target Conditionals */
#ifdef __APPLE__
#include <TargetConditionals.h>
#include <Availability.h>
#endif
/* Special configuration for AppleTVOS */
#if TARGET_OS_TV
#undef HAVE_SYSCALL_H
#undef HAVE_SYS_SYSCALL_H
#undef OS_MACOSX
/* NO_THREADS needed right now for Xcode 16 */
#define NO_THREADS
#endif
/* Special configuration for ucontext */
#undef HAVE_UCONTEXT_H
#undef PC_FROM_UCONTEXT
#if defined(__x86_64__)
#define PC_FROM_UCONTEXT uc_mcontext->__ss.__rip
#elif defined(__i386__)
#define PC_FROM_UCONTEXT uc_mcontext->__ss.__eip
#endif
EOF
# Prepare exported header include
EXPORTED_INCLUDE_DIR="exported/glog"
mkdir -p exported/glog
cp -f src/glog/log_severity.h "$EXPORTED_INCLUDE_DIR/"
cp -f src/glog/logging.h "$EXPORTED_INCLUDE_DIR/"
cp -f src/glog/raw_logging.h "$EXPORTED_INCLUDE_DIR/"
cp -f src/glog/stl_logging.h "$EXPORTED_INCLUDE_DIR/"
cp -f src/glog/vlog_is_on.h "$EXPORTED_INCLUDE_DIR/"

18
node_modules/react-native/scripts/node-binary.sh generated vendored Normal file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
# 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.
[ -z "$NODE_BINARY" ] && export NODE_BINARY="node"
nodejs_not_found()
{
echo "error: Can't find the '$NODE_BINARY' binary to build the React Native bundle. " \
"If you have a non-standard Node.js installation, select your project in Xcode, find " \
"'Build Phases' - 'Bundle React Native code and images' and change NODE_BINARY to an " \
"absolute path to your node executable. You can find it by invoking 'which node' in the terminal." >&2
exit 2
}
type "$NODE_BINARY" >/dev/null 2>&1 || nodejs_not_found

33
node_modules/react-native/scripts/packager-reporter.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
/**
* 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';
let reporter /*: $FlowFixMe */;
const logPath = process.env.RCT_PACKAGER_LOG_PATH;
if (logPath != null && logPath !== '') {
const {JsonReporter} = require('metro');
const fs = require('fs');
const path = require('path');
// $FlowFixMe[missing-type-arg]
reporter = class extends JsonReporter {
constructor() {
fs.mkdirSync(path.dirname(logPath), {
recursive: true,
});
super(fs.createWriteStream(logPath));
}
};
} else {
reporter = require('metro').TerminalReporter;
}
module.exports = reporter;

19
node_modules/react-native/scripts/packager.sh generated vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# 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.
# scripts directory
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
REACT_NATIVE_ROOT="$THIS_DIR/.."
# Application root directory - General use case: react-native is a dependency
PROJECT_ROOT=${PROJECT_ROOT:-"$THIS_DIR/../../.."}
# check and assign NODE_BINARY env
# shellcheck disable=SC1090
source "${THIS_DIR}/node-binary.sh"
# Start packager from PROJECT_ROOT
cd "$PROJECT_ROOT" || exit
"$NODE_BINARY" "$REACT_NATIVE_ROOT/cli.js" start --custom-log-reporter-path "$THIS_DIR/packager-reporter.js" "$@"

192
node_modules/react-native/scripts/react-native-xcode.sh generated vendored Executable file
View File

@@ -0,0 +1,192 @@
#!/bin/bash
# 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.
# Bundle React Native app's code and image assets.
# This script is supposed to be invoked as part of Xcode build process
# and relies on environment variables (including PWD) set by Xcode
# Print commands before executing them (useful for troubleshooting)
set -x -e
DEST="$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH"
# Enables iOS devices to get the IP address of the machine running Metro
if [[ ! "$SKIP_BUNDLING_METRO_IP" && "$CONFIGURATION" = *Debug* && ! "$PLATFORM_NAME" == *simulator ]]; then
for num in 0 1 2 3 4 5 6 7 8; do
IP=$(ipconfig getifaddr en${num} || echo "")
if [ ! -z "$IP" ]; then
break
fi
done
if [ -z "$IP" ]; then
IP=$(ifconfig | grep 'inet ' | grep -v ' 127.' | grep -v ' 169.254.' |cut -d\ -f2 | awk 'NR==1{print $1}')
fi
echo "$IP" > "$DEST/ip.txt"
fi
if [[ "$SKIP_BUNDLING" ]]; then
echo "SKIP_BUNDLING enabled; skipping."
exit 0;
fi
case "$CONFIGURATION" in
*Debug*)
if [[ "$PLATFORM_NAME" == *simulator ]]; then
if [[ "$FORCE_BUNDLING" ]]; then
echo "FORCE_BUNDLING enabled; continuing to bundle."
else
echo "Skipping bundling in Debug for the Simulator (since the packager bundles for you). Use the FORCE_BUNDLING flag to change this behavior."
exit 0;
fi
else
echo "Bundling for physical device. Use the SKIP_BUNDLING flag to change this behavior."
fi
DEV=true
;;
"")
echo "$0 must be invoked by Xcode"
exit 1
;;
*)
DEV=false
;;
esac
# Path to react-native folder inside node_modules
REACT_NATIVE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# Most projects have their project root, one level up from their Xcode project dir (the "ios" directory)
PROJECT_ROOT="${PROJECT_ROOT:-"$PROJECT_DIR/.."}"
cd "$PROJECT_ROOT" || exit
# Define entry file
if [[ "$ENTRY_FILE" ]]; then
# Use ENTRY_FILE defined by user
:
elif [[ -s "index.ios.js" ]]; then
ENTRY_FILE=${1:-index.ios.js}
else
ENTRY_FILE=${1:-index.js}
fi
# check and assign NODE_BINARY env
# shellcheck source=/dev/null
source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine"
[ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc"
# If hermesc is not available and USE_HERMES is not set to false, show error.
if [[ $USE_HERMES != false && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then
echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \
"Perhaps you need to run 'bundle exec pod install' or otherwise " \
"point the HERMES_CLI_PATH variable to your custom location." >&2
exit 2
fi
[ -z "$NODE_ARGS" ] && export NODE_ARGS=""
[ -z "$CLI_PATH" ] && CLI_PATH="$REACT_NATIVE_DIR/scripts/bundle.js"
[ -z "$BUNDLE_COMMAND" ] && BUNDLE_COMMAND="bundle"
[ -z "$COMPOSE_SOURCEMAP_PATH" ] && COMPOSE_SOURCEMAP_PATH="$REACT_NATIVE_DIR/scripts/compose-source-maps.js"
if [[ -z "$BUNDLE_CONFIG" ]]; then
CONFIG_ARG=""
else
CONFIG_ARG="--config $BUNDLE_CONFIG"
fi
if [[ -z "$BUNDLE_NAME" ]]; then
BUNDLE_NAME="main"
fi
BUNDLE_FILE="$CONFIGURATION_BUILD_DIR/$BUNDLE_NAME.jsbundle"
EXTRA_ARGS=()
case "$PLATFORM_NAME" in
"macosx")
BUNDLE_PLATFORM="macos"
;;
*)
BUNDLE_PLATFORM="ios"
;;
esac
if [ "${IS_MACCATALYST}" = "YES" ]; then
BUNDLE_PLATFORM="ios"
fi
EMIT_SOURCEMAP=
if [[ ! -z "$SOURCEMAP_FILE" ]]; then
EMIT_SOURCEMAP=true
fi
PACKAGER_SOURCEMAP_FILE=
if [[ $EMIT_SOURCEMAP == true ]]; then
if [[ $USE_HERMES != false ]]; then
PACKAGER_SOURCEMAP_FILE="$CONFIGURATION_BUILD_DIR/$(basename "$SOURCEMAP_FILE")"
else
PACKAGER_SOURCEMAP_FILE="$SOURCEMAP_FILE"
fi
EXTRA_ARGS+=("--sourcemap-output" "$PACKAGER_SOURCEMAP_FILE")
fi
# Hermes doesn't require JS minification.
if [[ $USE_HERMES != false && $DEV == false ]]; then
EXTRA_ARGS+=("--minify" "false")
fi
# Allow opting out of using npx react-native config
if [[ -n "$CONFIG_JSON" ]]; then
EXTRA_ARGS+=("--load-config" "$CONFIG_JSON")
elif [[ -n "$CONFIG_CMD" ]]; then
EXTRA_ARGS+=("--config-cmd" "$CONFIG_CMD")
else
EXTRA_ARGS+=("--config-cmd" "'$NODE_BINARY' $NODE_ARGS '$REACT_NATIVE_DIR/cli.js' config")
fi
# shellcheck disable=SC2086
"$NODE_BINARY" $NODE_ARGS "$CLI_PATH" $BUNDLE_COMMAND \
$CONFIG_ARG \
--entry-file "$ENTRY_FILE" \
--platform "$BUNDLE_PLATFORM" \
--dev $DEV \
--reset-cache \
--bundle-output "$BUNDLE_FILE" \
--assets-dest "$DEST" \
"${EXTRA_ARGS[@]}" \
$EXTRA_PACKAGER_ARGS
if [[ $USE_HERMES == false ]]; then
cp "$BUNDLE_FILE" "$DEST/"
BUNDLE_FILE="$DEST/$BUNDLE_NAME.jsbundle"
else
EXTRA_COMPILER_ARGS=
if [[ $DEV == true ]]; then
EXTRA_COMPILER_ARGS=-Og
else
EXTRA_COMPILER_ARGS=-O
fi
if [[ $EMIT_SOURCEMAP == true ]]; then
EXTRA_COMPILER_ARGS="$EXTRA_COMPILER_ARGS -output-source-map"
fi
"$HERMES_CLI_PATH" -emit-binary -max-diagnostic-width=80 $EXTRA_COMPILER_ARGS -out "$DEST/$BUNDLE_NAME.jsbundle" "$BUNDLE_FILE"
if [[ $EMIT_SOURCEMAP == true ]]; then
HBC_SOURCEMAP_FILE="$DEST/$BUNDLE_NAME.jsbundle.map"
"$NODE_BINARY" "$COMPOSE_SOURCEMAP_PATH" "$PACKAGER_SOURCEMAP_FILE" "$HBC_SOURCEMAP_FILE" -o "$SOURCEMAP_FILE"
rm "$HBC_SOURCEMAP_FILE"
rm "$PACKAGER_SOURCEMAP_FILE"
fi
BUNDLE_FILE="$DEST/$BUNDLE_NAME.jsbundle"
fi
if [[ $DEV != true && ! -f "$BUNDLE_FILE" ]]; then
echo "error: File $BUNDLE_FILE does not exist. Your environment is misconfigured as Metro was not able to produce the bundle so your release application won't work!" >&2
exit 2
fi

557
node_modules/react-native/scripts/react_native_pods.rb generated vendored Normal file
View File

@@ -0,0 +1,557 @@
# 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.
require 'json'
require 'open3'
require 'pathname'
require_relative './react_native_pods_utils/script_phases.rb'
require_relative './cocoapods/jsengine.rb'
require_relative './cocoapods/rndependencies.rb'
require_relative './cocoapods/rncore.rb'
require_relative './cocoapods/fabric.rb'
require_relative './cocoapods/codegen.rb'
require_relative './cocoapods/codegen_utils.rb'
require_relative './cocoapods/utils.rb'
require_relative './cocoapods/new_architecture.rb'
require_relative './cocoapods/local_podspec_patch.rb'
require_relative './cocoapods/runtime.rb'
require_relative './cocoapods/helpers.rb'
require_relative './cocoapods/privacy_manifest_utils.rb'
require_relative './cocoapods/spm.rb'
require_relative './cocoapods/rncore.rb'
# Importing to expose use_native_modules!
require_relative './cocoapods/autolinking.rb'
$CODEGEN_OUTPUT_DIR = 'build/generated/ios/ReactCodegen'
$APP_DEPENDENCY_PROVIDER_OUTPUT_DIR = 'build/generated/ios/ReactAppDependencyProvider'
$CODEGEN_COMPONENT_DIR = 'react/renderer/components'
$CODEGEN_MODULE_DIR = '.'
$START_TIME = Time.now.to_i
def min_ios_version_supported
return Helpers::Constants.min_ios_version_supported
end
# This function returns the min supported OS versions supported by React Native
# By using this function, you won't have to manually change your Podfile
# when we change the minimum version supported by the framework.
def min_supported_versions
return { :ios => min_ios_version_supported }
end
# This function prepares the project for React Native, before processing
# all the target exposed by the framework.
def prepare_react_native_project!
# Temporary solution to suppress duplicated GUID error & master specs repo warning.
# Can be removed once we move to generate files outside pod install.
install! 'cocoapods', :deterministic_uuids => false, :warn_for_unused_master_specs_repo => false
ReactNativePodsUtils.create_xcode_env_if_missing
end
# Function that setup all the react native dependencies
# 
# Parameters
# - path: path to react_native installation.
# - fabric_enabled: whether fabric should be enabled or not.
# - new_arch_enabled: [DEPRECATED] whether the new architecture should be enabled or not.
# - :production [DEPRECATED] whether the dependencies must be installed to target a Debug or a Release build.
# - hermes_enabled: whether Hermes should be enabled or not.
# - app_path: path to the React Native app. Required by the New Architecture.
# - config_file_dir: directory of the `package.json` file, required by the New Architecture.
def use_react_native! (
path: "../node_modules/react-native",
fabric_enabled: false,
new_arch_enabled: NewArchitectureHelper.new_arch_enabled,
production: false, # deprecated
hermes_enabled: true, # deprecated. Hermes is the default engine and JSC has been moved to community support
app_path: '..',
config_file_dir: '',
privacy_file_aggregation_enabled: true
)
error_if_try_to_use_jsc_from_core()
warn_if_new_arch_disabled()
hermes_enabled= true
# Set the app_path as env variable so the podspecs can access it.
ENV['APP_PATH'] = app_path
ENV['REACT_NATIVE_PATH'] = path
# We set RCT_SKIP_CODEGEN to true, if the user wants to skip the running Codegen step from Cocoapods.
# This is needed as part of our migration away from cocoapods
ENV['RCT_SKIP_CODEGEN'] = ENV['RCT_SKIP_CODEGEN'] == '1' || ENV['RCT_IGNORE_PODS_DEPRECATION'] == '1' ? '1' : '0'
ReactNativePodsUtils.check_minimum_required_xcode()
# Current target definition is provided by Cocoapods and it refers to the target
# that has invoked the `use_react_native!` function.
ReactNativePodsUtils.detect_use_frameworks(current_target_definition)
CodegenUtils.clean_up_build_folder(path, $CODEGEN_OUTPUT_DIR)
# We are relying on this flag also in third parties libraries to proper install dependencies.
# Better to rely and enable this environment flag if the new architecture is turned on using flags.
relative_path_from_current = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
react_native_version = NewArchitectureHelper.extract_react_native_version(File.join(relative_path_from_current, path))
fabric_enabled = true
ENV['RCT_FABRIC_ENABLED'] = "1"
ENV['RCT_AGGREGATE_PRIVACY_FILES'] = privacy_file_aggregation_enabled ? "1" : "0"
ENV["RCT_NEW_ARCH_ENABLED"] = "1"
prefix = path
ReactNativePodsUtils.warn_if_not_on_arm64()
# Update ReactNativeDependencies so that we can easily switch between source and prebuilt
ReactNativeDependenciesUtils.setup_react_native_dependencies(prefix, react_native_version)
# Update ReactNativeCoreUtils so that we can easily switch between source and prebuilt
ReactNativeCoreUtils.setup_rncore(prefix, react_native_version)
Pod::UI.puts "Configuring the target with the New Architecture\n"
# The Pods which should be included in all projects
pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector"
pod 'RCTRequired', :path => "#{prefix}/Libraries/Required"
pod 'RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :modular_headers => true
pod 'React', :path => "#{prefix}/"
if !ReactNativeCoreUtils.build_rncore_from_source()
pod 'React-Core-prebuilt', :podspec => "#{prefix}/React-Core-prebuilt.podspec", :modular_headers => true
end
pod 'React-Core', :path => "#{prefix}/"
pod 'React-CoreModules', :path => "#{prefix}/React/CoreModules"
pod 'React-RCTRuntime', :path => "#{prefix}/React/Runtime"
pod 'React-RCTAppDelegate', :path => "#{prefix}/Libraries/AppDelegate"
pod 'React-RCTActionSheet', :path => "#{prefix}/Libraries/ActionSheetIOS"
pod 'React-RCTAnimation', :path => "#{prefix}/Libraries/NativeAnimation"
pod 'React-RCTBlob', :path => "#{prefix}/Libraries/Blob"
pod 'React-RCTImage', :path => "#{prefix}/Libraries/Image"
pod 'React-RCTLinking', :path => "#{prefix}/Libraries/LinkingIOS"
pod 'React-RCTNetwork', :path => "#{prefix}/Libraries/Network"
pod 'React-RCTSettings', :path => "#{prefix}/Libraries/Settings"
pod 'React-RCTText', :path => "#{prefix}/Libraries/Text"
pod 'React-RCTVibration', :path => "#{prefix}/Libraries/Vibration"
pod 'React-Core/RCTWebSocket', :path => "#{prefix}/"
pod 'React-cxxreact', :path => "#{prefix}/ReactCommon/cxxreact"
pod 'React-debug', :path => "#{prefix}/ReactCommon/react/debug"
pod 'React-utils', :path => "#{prefix}/ReactCommon/react/utils"
pod 'React-featureflags', :path => "#{prefix}/ReactCommon/react/featureflags"
pod 'React-featureflagsnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/featureflags"
pod 'React-microtasksnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/microtasks"
pod 'React-idlecallbacksnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/idlecallbacks"
pod 'React-intersectionobservernativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/intersectionobserver"
pod 'React-webperformancenativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/webperformance"
pod 'React-domnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/dom"
pod 'React-defaultsnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/defaults"
pod 'React-Mapbuffer', :path => "#{prefix}/ReactCommon"
pod 'React-jserrorhandler', :path => "#{prefix}/ReactCommon/jserrorhandler"
pod 'RCTDeprecation', :path => "#{prefix}/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
pod 'React-RCTFBReactNativeSpec', :path => "#{prefix}/React"
pod 'React-jsi', :path => "#{prefix}/ReactCommon/jsi"
pod 'RCTSwiftUI', :path => "#{prefix}/ReactApple/RCTSwiftUI"
pod 'RCTSwiftUIWrapper', :path => "#{prefix}/ReactApple/RCTSwiftUIWrapper"
if hermes_enabled
setup_hermes!(:react_native_path => prefix)
end
pod 'React-jsiexecutor', :path => "#{prefix}/ReactCommon/jsiexecutor"
pod 'React-jsinspector', :path => "#{prefix}/ReactCommon/jsinspector-modern"
pod 'React-jsitooling', :path => "#{prefix}/ReactCommon/jsitooling"
pod 'React-jsinspectorcdp', :path => "#{prefix}/ReactCommon/jsinspector-modern/cdp"
pod 'React-jsinspectornetwork', :path => "#{prefix}/ReactCommon/jsinspector-modern/network"
pod 'React-jsinspectortracing', :path => "#{prefix}/ReactCommon/jsinspector-modern/tracing"
pod 'React-callinvoker', :path => "#{prefix}/ReactCommon/callinvoker"
pod 'React-networking', :path => "#{prefix}/ReactCommon/react/networking"
pod 'React-performancecdpmetrics', :path => "#{prefix}/ReactCommon/react/performance/cdpmetrics"
pod 'React-performancetimeline', :path => "#{prefix}/ReactCommon/react/performance/timeline"
pod 'React-timing', :path => "#{prefix}/ReactCommon/react/timing"
pod 'React-runtimeexecutor', :path => "#{prefix}/ReactCommon/runtimeexecutor"
pod 'React-runtimescheduler', :path => "#{prefix}/ReactCommon/react/renderer/runtimescheduler"
pod 'React-renderercss', :path => "#{prefix}/ReactCommon/react/renderer/css"
pod 'React-rendererdebug', :path => "#{prefix}/ReactCommon/react/renderer/debug"
pod 'React-rendererconsistency', :path => "#{prefix}/ReactCommon/react/renderer/consistency"
pod 'React-perflogger', :path => "#{prefix}/ReactCommon/reactperflogger"
pod 'React-oscompat', :path => "#{prefix}/ReactCommon/oscompat"
pod 'React-logger', :path => "#{prefix}/ReactCommon/logger"
pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :modular_headers => true
pod 'React-NativeModulesApple', :path => "#{prefix}/ReactCommon/react/nativemodule/core/platform/ios", :modular_headers => true
pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true
setup_fabric!(:react_native_path => prefix)
setup_bridgeless!(:react_native_path => prefix, :use_hermes => hermes_enabled)
if ReactNativeDependenciesUtils.build_react_native_deps_from_source()
pod 'DoubleConversion', :podspec => "#{prefix}/third-party-podspecs/DoubleConversion.podspec"
pod 'glog', :podspec => "#{prefix}/third-party-podspecs/glog.podspec"
pod 'boost', :podspec => "#{prefix}/third-party-podspecs/boost.podspec"
pod 'fast_float', :podspec => "#{prefix}/third-party-podspecs/fast_float.podspec"
pod 'fmt', :podspec => "#{prefix}/third-party-podspecs/fmt.podspec", :modular_headers => true
pod 'RCT-Folly', :podspec => "#{prefix}/third-party-podspecs/RCT-Folly.podspec", :modular_headers => true
pod 'SocketRocket', "~> #{Helpers::Constants::socket_rocket_config[:version]}", :modular_headers => true
else
# Install prebuilt React Native Core and React Native Dependencies
ReactNativeCoreUtils.rncore_log("Using React Native Core and React Native Dependencies prebuilt versions.")
pod 'ReactNativeDependencies', :podspec => "#{prefix}/third-party-podspecs/ReactNativeDependencies.podspec", :modular_headers => true
if !ReactNativeCoreUtils.build_rncore_from_source()
pod 'React-Core-prebuilt', :podspec => "#{prefix}/React-Core-prebuilt.podspec", :modular_headers => true
end
end
pod 'ReactCodegen', :path => $CODEGEN_OUTPUT_DIR, :modular_headers => true
pod 'ReactAppDependencyProvider', :path => $APP_DEPENDENCY_PROVIDER_OUTPUT_DIR, :modular_headers => true
# Not needed, but run_codegen expects this to be set.
folly_config = get_folly_config()
run_codegen!(
app_path,
config_file_dir,
:new_arch_enabled => NewArchitectureHelper.new_arch_enabled,
:disable_codegen => ENV['DISABLE_CODEGEN'] == '1',
:react_native_path => prefix,
:fabric_enabled => fabric_enabled,
:hermes_enabled => hermes_enabled,
:codegen_output_dir => $CODEGEN_OUTPUT_DIR,
:package_json_file => File.join(__dir__, "..", "package.json"),
:folly_version => folly_config[:version]
)
pods_to_update = LocalPodspecPatch.pods_to_update(:react_native_path => prefix)
if !pods_to_update.empty?
if Pod::Lockfile.public_instance_methods.include?(:detect_changes_with_podfile)
Pod::Lockfile.prepend(LocalPodspecPatch)
else
Pod::UI.warn "Automatically updating #{pods_to_update.join(", ")} has failed, please run `pod update #{pods_to_update.join(" ")} --no-repo-update` manually to fix the issue."
end
end
end
# Getter to retrieve the folly flags in case contributors need to apply them manually.
#
# Returns: the folly compiler flags
def folly_flags()
return NewArchitectureHelper.folly_compiler_flags
end
# Resolve the spec for use with the USE_FRAMEWORKS environment variable. To avoid each podspec
# to manually specify the header mappings and module name, we can use this helper function.
# This helper will also resolve header mappings if we're building from source. Precompiled
# React-Core will not generate frameworks since their podspec files only contains the
# header files and no source code - so header_mappings should be the same as for without USE_FRAMEWORKS
#
# Parameters:
# - s: the spec to modify
# - header_mappings_dir: the directory to map headers when building Pod header structure
# - module_name: the name of the module when exposed to swift
def resolve_use_frameworks(spec, header_mappings_dir: nil, module_name: nil)
ReactNativePodsUtils.resolve_use_frameworks(spec, :header_mappings_dir => header_mappings_dir, :module_name => module_name)
end
# Add a dependency to a spec, making sure that the HEADER_SERACH_PATHS are set properly.
# This function automate the requirement to specify the HEADER_SEARCH_PATHS which was error prone
# and hard to pull out properly to begin with.
# Secondly, it prepares the podspec to work also with other platforms, because this function is
# able to generate search paths that are compatible with macOS and other platform if specified by
# the $RN_PLATFORMS variable.
# To generate Header Search Paths for multiple platforms, define in your Podfile or Ruby infra a
# $RN_PLATFORMS static variable with the list of supported platforms, for example:
# `$RN_PLATFORMS = ["iOS", "macOS"]`
#
# Parameters:
# - spec: the spec that needs to be modified
# - pod_name: the name of the dependency we had to add to the spec
# - additional_framework_paths: additional sub paths we had to add to the HEADER_SEARCH_PATH
# - framework_name: the name of the framework in case it is different from the pod_name
# - version: the version of the pod_name the spec needs to depend on
# - base_dir: Base directory from where we need to start looking. Defaults to PODS_CONFIGURATION_BUILD_DIR
def add_dependency(spec, pod_name, subspec: nil, additional_framework_paths: [], framework_name: nil, version: nil, base_dir: "PODS_CONFIGURATION_BUILD_DIR")
fixed_framework_name = framework_name != nil ? framework_name : pod_name.gsub("-", "_") # frameworks can't have "-" in their name
ReactNativePodsUtils.add_dependency(spec, pod_name, base_dir, fixed_framework_name, :additional_paths => additional_framework_paths, :version => version)
end
# This function generates an array of HEADER_SEARCH_PATH that can be added to the HEADER_SEARCH_PATH property when use_frameworks! is enabled
#
# Parameters:
# - pod_name: the name of the dependency we had to add to the spec
# - additional_framework_paths: additional sub paths we had to add to the HEADER_SEARCH_PATH
# - framework_name: the name of the framework in case it is different from the pod_name
# - base_dir: Base directory from where we need to start looking. Defaults to PODS_CONFIGURATION_BUILD_DIR
# - include_base_folder: whether the array must include the base import path or only the additional_framework_paths
def create_header_search_path_for_frameworks(pod_name, additional_framework_paths: [], framework_name: nil, base_dir: "PODS_CONFIGURATION_BUILD_DIR", include_base_folder: true)
fixed_framework_name = framework_name != nil ? framework_name : pod_name.gsub("-", "_")
return ReactNativePodsUtils.create_header_search_path_for_frameworks(base_dir, pod_name, fixed_framework_name, additional_framework_paths, include_base_folder)
end
# This function can be used by library developer to prepare their modules for the New Architecture.
# It passes the Folly Flags to the module, it configures the search path and installs some New Architecture specific dependencies.
#
# Parameters:
# - spec: The spec that has to be configured with the New Architecture code
# - new_arch_enabled: [DEPRECATED] Whether the module should install dependencies for the new architecture
def install_modules_dependencies(spec, new_arch_enabled: NewArchitectureHelper.new_arch_enabled)
folly_config = get_folly_config()
NewArchitectureHelper.install_modules_dependencies(spec, true, folly_config[:version])
end
# This function is used by podspecs that needs to use the prebuilt sources for React Native.
# It returns the sources to use for the podspec.
#
# Parameters:
# - original_sources: The original sources of the podspec
# - sources_for_prebuilds: The sources to use for prebuilt pods
#
# Returns: The sources to use for the podspec
def podspec_sources(original_sources, sources_for_prebuilds)
if ReactNativeCoreUtils.build_rncore_from_source()
return original_sources
else
return sources_for_prebuilds
end
end
# This function can be used by library developer to declare a SwiftPackageManager dependency.
#
# Parameters:
# - spec: The spec the Swift Package Manager dependency has to be added to
# - url: The URL of the Swift Package Manager dependency
# - requirement: The version requirement of the Swift Package Manager dependency (eg. ` {kind: 'upToNextMajorVersion', minimumVersion: '5.9.1'},`)
# - products: The product/target of the Swift Package Manager dependency (eg. AlamofireDynamic)
def spm_dependency(spec, url:, requirement:, products:)
SPM.dependency(spec, url: url, requirement: requirement, products: products)
end
# It returns the default flags.
# deprecated.
def get_default_flags()
warn 'get_default_flags is deprecated. Please remove the keys from the `use_react_native!` function'
warn 'if you are using the default already and pass the value you need in case you don\'t want the default'
return ReactNativePodsUtils.get_default_flags()
end
# This method returns an hash with the folly version, folly git url and the folly compiler flags
# that can be used to configure libraries.
# In this way, we can update those values in react native, and all the libraries will benefit
# from it.
# @return an hash with the `:version`, `:git` and `:compiler_flags` fields.
def get_folly_config()
return Helpers::Constants.folly_config
end
# This method returns an hash with the glog git url
# that can be used to configure libraries.
# @return an hash with the `:git` field.
def get_glog_config()
return Helpers::Constants.glog_config
end
# This method returns an hash with the fast_float git url
# that can be used to configure libraries.
# @return an hash with the `:git` field.
def get_fast_float_config()
return Helpers::Constants.fast_float_config
end
# This method returns an hash with the fmt git url
# that can be used to configure libraries.
# @return an hash with the `:git` field.
def get_fmt_config()
return Helpers::Constants.fmt_config
end
# This method returns an hash with the double conversion git url
# that can be used to configure libraries.
# @return an hash with the `:git` field.
def get_double_conversion_config()
return Helpers::Constants.double_conversion_config
end
# This method returns an hash with the double conversion git url
# that can be used to configure libraries.
# @return an hash with the `:git` field.
def get_boost_config()
return Helpers::Constants.boost_config
end
# This method returns an hash with the socket rocket version
# @return an hash with the `:version` field.
def get_socket_rocket_config()
return Helpers::Constants.socket_rocket_config
end
# This method can be used to set the glog config
# that can be used to configure libraries.
def set_folly_config(folly_config)
Helpers::Constants.set_folly_config(folly_config)
end
# This method can be used to set the glog config
# that can be used to configure libraries.
def set_glog_config(glog_config)
Helpers::Constants.set_glog_config(glog_config)
end
# This method can be used to set the fast_float config
# that can be used to configure libraries.
def set_fast_float_config(fast_float_config)
Helpers::Constants.set_fast_float_config(fast_float_config)
end
# This method can be used to set the fmt config
# that can be used to configure libraries.
def set_fmt_config(fmt_config)
Helpers::Constants.set_fmt_config(fmt_config)
end
# This method can be used to set the double conversion config
# that can be used to configure libraries.
def set_double_conversion_config(double_conversion_config)
Helpers::Constants.set_double_conversion_config(double_conversion_config)
end
# This method can be used to set the boost config
# that can be used to configure libraries.
def set_boost_config(boost_config)
Helpers::Constants.set_boost_config(boost_config)
end
# This method can be used to set the socket rocket config
# that can be used to configure libraries.
def set_socket_rocket_config(socket_rocket_config)
Helpers::Constants.set_socket_rocket_config(socket_rocket_config)
end
def rct_cxx_language_standard()
return Helpers::Constants.cxx_language_standard
end
def print_jsc_removal_message()
puts ''
puts '=============== JavaScriptCore is being moved ==============='.yellow
puts 'JavaScriptCore has been removed from React Native. It can now be'.yellow
puts 'installed from `@react-native-community/javascriptcore`'.yellow
puts 'See: https://github.com/react-native-community/javascriptcore'.yellow
puts '============================================================='.yellow
puts ''
end
def print_cocoapods_deprecation_message()
if ENV["RCT_IGNORE_PODS_DEPRECATION"] == "1"
return
end
puts ''
puts '==================== DEPRECATION NOTICE ====================='.yellow
puts 'Calling `pod install` directly is deprecated in React Native'.yellow
puts 'because we are moving away from Cocoapods toward alternative'.yellow
puts 'solutions to build the project.'.yellow
puts '* If you are using Expo, please run:'.yellow
puts '`npx expo run:ios`'.yellow
puts '* If you are using the Community CLI, please run:'.yellow
puts '`yarn ios`'.yellow
puts '============================================================='.yellow
puts ''
end
def warn_if_new_arch_disabled
if ENV["RCT_NEW_ARCH_ENABLED"] == "1" || ENV["RCT_NEW_ARCH_ENABLED"] == nil
return
end
puts ''
puts '====================== NEW ARCH ONLY ======================='.yellow
puts 'WARNING: Calling pod install with `RCT_NEW_ARCH_ENABLED=0'.yellow
puts 'is not supported anymore since React Native 0.82.'.yellow
puts 'You can remove the ENV["RCT_NEW_ARCH_ENABLED"]=0 from your'.yellow
puts 'Podfie, if you have it.'.yellow
puts 'The application will run with the New Architecture enabled'.yellow
puts 'by default.'.yellow
puts '============================================================'.yellow
puts ''
end
def error_if_try_to_use_jsc_from_core()
explicitly_not_use_hermes = ENV['USE_HERMES'] != nil && ENV['USE_HERMES'] == '0'
not_use_3rd_party_jsc = ENV['USE_THIRD_PARTY_JSC'] == nil || ENV['USE_THIRD_PARTY_JSC'] == '0'
if (explicitly_not_use_hermes && not_use_3rd_party_jsc)
message = "Hermes is the default engine and JSC has been moved to community support.\n" +
"Please remove the USE_HERMES=0, as it is not supported anymore.\n" +
"If you want to use JSC, you can install it from `@react-native-community/javascriptcore`.\n" +
"See: https://github.com/react-native-community/javascriptcore"
puts message.red
exit()
end
end
# Function that executes after React Native has been installed to configure some flags and build settings.
#
# Parameters
# - installer: the Cocoapod object that allows to customize the project.
# - react_native_path: path to React Native.
# - mac_catalyst_enabled: whether we are running the Pod on a Mac Catalyst project or not.
# - enable_hermes_profiler: whether the hermes profiler should be turned on in Release mode
def react_native_post_install(
installer,
react_native_path = "../node_modules/react-native",
mac_catalyst_enabled: false,
ccache_enabled: ENV['USE_CCACHE'] == '1'
)
ReactNativePodsUtils.turn_off_resource_bundle_react_core(installer)
ReactNativePodsUtils.apply_mac_catalyst_patches(installer) if mac_catalyst_enabled
privacy_file_aggregation_enabled = ENV['RCT_AGGREGATE_PRIVACY_FILES'] == '1'
if use_hermes()
ReactNativePodsUtils.set_gcc_preprocessor_definition_for_React_hermes(installer)
end
ReactNativePodsUtils.set_gcc_preprocessor_definition_for_debugger(installer)
ReactNativePodsUtils.fix_library_search_paths(installer)
ReactNativePodsUtils.update_search_paths(installer)
ReactNativePodsUtils.set_build_setting(installer, build_setting: "USE_HERMES", value: use_hermes())
ReactNativePodsUtils.set_build_setting(installer, build_setting: "REACT_NATIVE_PATH", value: File.join("${PODS_ROOT}", "..", react_native_path))
ReactNativePodsUtils.set_build_setting(installer, build_setting: "SWIFT_ACTIVE_COMPILATION_CONDITIONS", value: ['$(inherited)', 'DEBUG'], config_name: "Debug")
if (ENV['RCT_REMOVE_LEGACY_ARCH'] == '1')
ReactNativePodsUtils.add_compiler_flag_to_project(installer, "-DRCT_REMOVE_LEGACY_ARCH=1")
else
ReactNativePodsUtils.remove_compiler_flag_from_project(installer, "-DRCT_REMOVE_LEGACY_ARCH=1")
end
ReactNativePodsUtils.set_ccache_compiler_and_linker_build_settings(installer, react_native_path, ccache_enabled)
ReactNativePodsUtils.updateOSDeploymentTarget(installer)
ReactNativePodsUtils.set_dynamic_frameworks_flags(installer)
ReactNativePodsUtils.add_ndebug_flag_to_pods_in_release(installer)
if !ReactNativeCoreUtils.build_rncore_from_source()
# In XCode 26 we need to revert the new setting SWIFT_ENABLE_EXPLICIT_MODULES when building
# with precompiled binaries.
ReactNativePodsUtils.set_build_setting(installer, build_setting: "SWIFT_ENABLE_EXPLICIT_MODULES", value: "NO")
end
SPM.apply_on_post_install(installer)
if privacy_file_aggregation_enabled
PrivacyManifestUtils.add_aggregated_privacy_manifest(installer)
else
PrivacyManifestUtils.add_privacy_manifest_if_needed(installer)
end
NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer)
NewArchitectureHelper.modify_flags_for_new_architecture(installer, NewArchitectureHelper.new_arch_enabled)
NewArchitectureHelper.set_RCTNewArchEnabled_in_info_plist(installer, NewArchitectureHelper.new_arch_enabled)
if !use_hermes() && !use_third_party_jsc()
print_jsc_removal_message()
end
print_cocoapods_deprecation_message
Pod::UI.puts "Pod install took #{Time.now.to_i - $START_TIME} [s] to run".green
end

View File

@@ -0,0 +1,51 @@
# 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.
# Run test manually by running `ruby react-native/scripts/react_native_pods_utils/__tests__/script_phases.test.rb`
require "erb"
def get_script_phases_with_codegen_discovery(options)
export_vars = {
'RCT_SCRIPT_RN_DIR' => "$RCT_SCRIPT_POD_INSTALLATION_ROOT/#{options[:react_native_path]}",
'RCT_SCRIPT_APP_PATH' => "$RCT_SCRIPT_POD_INSTALLATION_ROOT/#{options[:relative_app_root]}",
'RCT_SCRIPT_OUTPUT_DIR' => "$RCT_SCRIPT_POD_INSTALLATION_ROOT",
'RCT_SCRIPT_TYPE' => "withCodegenDiscovery",
}
return get_script_template(options[:react_native_path], export_vars)
end
def get_script_phases_no_codegen_discovery(options)
export_vars = {
'RCT_SCRIPT_RN_DIR' => "${PODS_TARGET_SRCROOT}/#{options[:react_native_path]}",
'RCT_SCRIPT_LIBRARY_NAME' => "#{options[:library_name]}",
'RCT_SCRIPT_OUTPUT_DIR' => "$RCT_SCRIPT_POD_INSTALLATION_ROOT/#{options[:codegen_output_dir]}",
'RCT_SCRIPT_LIBRARY_TYPE' => "#{options[:library_type] ? options[:library_type] : 'all'}",
'RCT_SCRIPT_JS_SRCS_PATTERN' => "#{options[:js_srcs_pattern]}",
'RCT_SCRIPT_JS_SRCS_DIR' => "#{options[:js_srcs_dir]}",
'RCT_SCRIPT_CODEGEN_MODULE_DIR' => "#{options[:codegen_module_dir]}",
'RCT_SCRIPT_CODEGEN_COMPONENT_DIR' => "#{options[:codegen_component_dir]}",
'RCT_SCRIPT_FILE_LIST' => ("#{options[:file_list]}").dump,
}
return get_script_template(options[:react_native_path], export_vars)
end
def get_script_template(react_native_path, export_vars={})
template =<<~EOS
pushd "$PODS_ROOT/../" > /dev/null
RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd)
popd >/dev/null
<% export_vars.each do |(varname, value)| %>
export <%= varname -%>=<%= value -%>
<% end %>
SCRIPT_PHASES_SCRIPT="$RCT_SCRIPT_RN_DIR/scripts/react_native_pods_utils/script_phases.sh"
WITH_ENVIRONMENT="$RCT_SCRIPT_RN_DIR/scripts/xcode/with-environment.sh"
/bin/sh -c "$WITH_ENVIRONMENT $SCRIPT_PHASES_SCRIPT"
EOS
result = ERB.new(template, trim_mode: '->').result(binding)
return result
end

View File

@@ -0,0 +1,133 @@
#!/bin/bash
# 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.
set -o pipefail
set -e
GENERATED_SRCS_DIR="$DERIVED_FILE_DIR/generated/source/codegen"
TEMP_OUTPUT_DIR="$GENERATED_SRCS_DIR/out"
GENERATED_SCHEMA_FILE="$GENERATED_SRCS_DIR/schema.json"
cd "$RCT_SCRIPT_RN_DIR"
CODEGEN_CLI_PATH=""
error () {
echo "$1"
"[Codegen] $1" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1
exit 1
}
find_node () {
NODE_BINARY="${NODE_BINARY:-$(command -v node || true)}"
if [ -z "$NODE_BINARY" ]; then
error "[Error] Could not find node. It looks like that the .xcode.env or .xcode.env.local " \
"files are misconfigured. Please check that they are exporting a valid NODE_BINARY " \
"variable, pointing to a node executable."
fi
}
find_codegen () {
CODEGEN_CLI_PATH=$("$NODE_BINARY" --print "require('path').dirname(require.resolve('@react-native/codegen/package.json'))")
if ! [ -d "$CODEGEN_CLI_PATH" ]; then
error "error: Could not determine @react-native/codegen location, using node module resolution. Try running 'yarn install' or 'npm install' in your project root."
fi
}
setup_dirs () {
set +e
rm -rf "$GENERATED_SRCS_DIR"
set -e
mkdir -p "$GENERATED_SRCS_DIR" "$TEMP_OUTPUT_DIR"
# Clear output files
true > "${SCRIPT_OUTPUT_FILE_0}"
}
describe () {
message="
>>>>> $1
"
echo "$message" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1
}
runSpecCodegen () {
"$NODE_BINARY" "scripts/generate-specs-cli.js" --platform ios --schemaPath "$GENERATED_SCHEMA_FILE" --outputDir "$1" --libraryName "$RCT_SCRIPT_LIBRARY_NAME" --libraryType "$2"
}
generateCodegenSchemaFromJavaScript () {
describe "Generating codegen schema from JavaScript"
SRCS_PATTERN="$RCT_SCRIPT_JS_SRCS_PATTERN"
SRCS_DIR="$RCT_SCRIPT_JS_SRCS_DIR"
if [ "$SRCS_PATTERN" ]; then
JS_SRCS=$(find "$PODS_TARGET_SRCROOT/$SRCS_DIR" -type f -name "$SRCS_PATTERN" -print0 | xargs -0)
echo "$RCT_SCRIPT_FILE_LIST" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1
else
JS_SRCS="$PODS_TARGET_SRCROOT/$SRCS_DIR"
echo "$RCT_SCRIPT_JS_SRCS_DIR" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1
fi
# shellcheck disable=SC2086
# $JS_SRCS not having double quotations is intentional
"$NODE_BINARY" "$CODEGEN_CLI_PATH/lib/cli/combine/combine-js-to-schema-cli.js" --exclude NativeSampleTurboModule "$GENERATED_SCHEMA_FILE" $JS_SRCS
}
generateCodegenArtifactsFromSchema () {
describe "Generating codegen artifacts from schema"
pushd "$RN_DIR" >/dev/null || exit 1
if [ "$RCT_SCRIPT_LIBRARY_TYPE" = "all" ]; then
runSpecCodegen "$TEMP_OUTPUT_DIR" "modules"
runSpecCodegen "$TEMP_OUTPUT_DIR" "components"
elif [ "$RCT_SCRIPT_LIBRARY_TYPE" = "components" ]; then
runSpecCodegen "$TEMP_OUTPUT_DIR" "$RCT_SCRIPT_LIBRARY_TYPE"
elif [ "$RCT_SCRIPT_LIBRARY_TYPE" = "modules" ]; then
runSpecCodegen "$TEMP_OUTPUT_DIR" "$RCT_SCRIPT_LIBRARY_TYPE"
fi
popd >/dev/null || exit 1
}
generateArtifacts () {
describe "Generating codegen artifacts"
pushd "$RCT_SCRIPT_RN_DIR" >/dev/null || exit 1
"$NODE_BINARY" "scripts/generate-codegen-artifacts.js" --path "$RCT_SCRIPT_APP_PATH" --outputPath "$RCT_SCRIPT_OUTPUT_DIR" --targetPlatform "ios"
popd >/dev/null || exit 1
}
moveOutputs () {
mkdir -p "$RCT_SCRIPT_OUTPUT_DIR"
# Copy all output to output_dir
/bin/cp -R -X "$TEMP_OUTPUT_DIR/." "$RCT_SCRIPT_OUTPUT_DIR" || exit 1
echo "$LIBRARY_NAME output has been written to $RCT_SCRIPT_OUTPUT_DIR:" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1
ls -1 "$RCT_SCRIPT_OUTPUT_DIR" >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1
}
withCodegenDiscovery () {
find_node
find_codegen
generateArtifacts
}
noCodegenDiscovery () {
setup_dirs
find_node
find_codegen
generateCodegenSchemaFromJavaScript
generateCodegenArtifactsFromSchema
moveOutputs
}
if [ "$RCT_SCRIPT_TYPE" = "withCodegenDiscovery" ]; then
withCodegenDiscovery "$@"
else
noCodegenDiscovery "$@"
fi
echo 'Done.' >> "${SCRIPT_OUTPUT_FILE_0}" 2>&1

View File

@@ -0,0 +1,122 @@
/**
* 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';
const {spawnSync} = require('child_process');
const fs = require('fs');
const yargs = require('yargs');
const LAST_BUILD_FILENAME = 'React-Core-prebuilt/.last_build_configuration';
function validateBuildConfiguration(configuration /*: string */) {
if (!['Debug', 'Release'].includes(configuration)) {
throw new Error(`Invalid configuration ${configuration}`);
}
}
function validateVersion(version /*: ?string */) {
if (version == null || version === '') {
throw new Error('Version cannot be empty');
}
}
function shouldReplaceRnCoreConfiguration(configuration /*: string */) {
const fileExists = fs.existsSync(LAST_BUILD_FILENAME);
if (fileExists) {
console.log(`Found ${LAST_BUILD_FILENAME} file`);
const oldConfiguration = fs.readFileSync(LAST_BUILD_FILENAME).toString();
if (oldConfiguration === configuration) {
console.log(
'Same config of the previous build. No need to replace React-Core-prebuilt',
);
return false;
}
}
// Assumption: if there is no stored last build, we assume that it was build for debug.
if (!fileExists && configuration === 'Debug') {
console.log(
'No previous build detected, but Debug Configuration. No need to replace React-Core-prebuilt',
);
return false;
}
return true;
}
function replaceRNCoreConfiguration(
configuration /*: string */,
version /*: string */,
podsRoot /*: string */,
) {
// Filename comes from rncore.rb
const tarballURLPath = `${podsRoot}/ReactNativeCore-artifacts/reactnative-core-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`;
const finalLocation = 'React-Core-prebuilt';
console.log('Preparing the final location', finalLocation);
fs.rmSync(finalLocation, {force: true, recursive: true});
fs.mkdirSync(finalLocation, {recursive: true});
console.log('Extracting the tarball', tarballURLPath);
spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], {
stdio: 'inherit',
});
}
function updateLastBuildConfiguration(configuration /*: string */) {
console.log(`Updating ${LAST_BUILD_FILENAME} with ${configuration}`);
fs.writeFileSync(LAST_BUILD_FILENAME, configuration);
}
function main(
configuration /*: string */,
version /*: string */,
podsRoot /*: string */,
) {
validateBuildConfiguration(configuration);
validateVersion(version);
if (!shouldReplaceRnCoreConfiguration(configuration)) {
return;
}
replaceRNCoreConfiguration(configuration, version, podsRoot);
updateLastBuildConfiguration(configuration);
console.log('Done replacing React Native prebuilt');
}
// This script is executed in the Pods folder, which is usually not synched to Github, so it should be ok
const argv = yargs
.option('c', {
alias: 'configuration',
description:
'Configuration to use to download the right React-Core prebuilt version. Allowed values are "Debug" and "Release".',
})
.option('r', {
alias: 'reactNativeVersion',
description:
'The Version of React Native associated with the React-Core prebuilt tarball.',
})
.option('p', {
alias: 'podsRoot',
description: 'The path to the Pods root folder',
})
.usage('Usage: $0 -c Debug -r <version> -p <path/to/react-native>').argv;
// $FlowFixMe[prop-missing]
const configuration = argv.configuration;
// $FlowFixMe[prop-missing]
const version = argv.reactNativeVersion;
// $FlowFixMe[prop-missing]
const podsRoot = argv.podsRoot;
main(configuration, version, podsRoot);

14
node_modules/react-native/scripts/xcode/ccache-clang++.sh generated vendored Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# 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.
# Get the absolute path of this script
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
REACT_NATIVE_CCACHE_CONFIGPATH=$SCRIPT_DIR/ccache.conf
# Provide our config file if none is already provided
export CCACHE_CONFIGPATH="${CCACHE_CONFIGPATH:-$REACT_NATIVE_CCACHE_CONFIGPATH}"
exec $CCACHE_BINARY clang++ "$@"

14
node_modules/react-native/scripts/xcode/ccache-clang.sh generated vendored Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# 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.
# Get the absolute path of this script
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
REACT_NATIVE_CCACHE_CONFIGPATH=$SCRIPT_DIR/ccache.conf
# Provide our config file if none is already provided
export CCACHE_CONFIGPATH="${CCACHE_CONFIGPATH:-$REACT_NATIVE_CCACHE_CONFIGPATH}"
exec $CCACHE_BINARY clang "$@"

11
node_modules/react-native/scripts/xcode/ccache.conf generated vendored Normal file
View File

@@ -0,0 +1,11 @@
# 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.
# See https://ccache.dev/manual/4.3.html#_configuration_options for details and available options
sloppiness = clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
file_clone = true
depend_mode = true
inode_cache = true

47
node_modules/react-native/scripts/xcode/with-environment.sh generated vendored Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
# 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.
# This script is used to source in Xcode the environment settings required to run properly.
# The script first sources the base `.xcode.env` file.
# Then it sources the `.xcode.env.local` file if present, to override some local config
# Finally, it will execute the command passed i input if any.
#
# USAGE:
# ./with-environment.sh command
# Start with a default
NODE_BINARY=$(command -v node || echo "")
export NODE_BINARY
# Override the default with the global environment
ENV_PATH="$PODS_ROOT/../.xcode.env"
if [ -f "$ENV_PATH" ]; then
source "$ENV_PATH"
fi
# Override the global with the local environment
LOCAL_ENV_PATH="${ENV_PATH}.local"
if [ -f "$LOCAL_ENV_PATH" ]; then
source "$LOCAL_ENV_PATH"
fi
# Check whether NODE_BINARY has been properly set, otherwise help the users with a meaningful error.
if [ -n "$NODE_BINARY" ]; then
echo "Node found at: ${NODE_BINARY}"
else
echo '[Warning] You need to configure your node path in the `".xcode.env" file` environment. ' \
'You can set it up quickly by running: ' \
'`echo export NODE_BINARY=$(command -v node) > .xcode.env` ' \
'in the ios folder. This is needed by React Native to work correctly. ' \
'We fallback to the DEPRECATED behavior of finding `node`. This will be REMOVED in a future version. ' \
'You can read more about this here: https://reactnative.dev/docs/environment-setup#optional-configuring-your-environment' >&2
source "${REACT_NATIVE_PATH}/scripts/find-node-for-xcode.sh"
fi
# Execute argument, if present
if [ -n "$1" ]; then
"$1"
fi