feat: add support for bridgeless mode (#1793)

Starting with 0.73, bridgeless mode can now be enabled:

- **Android:** In `gradle.properties`:
  ```gradle
  newArchEnabled=true
  bridgelessEnabled=true
  ```

- **iOS:** In `Podfile`:
  ```ruby
  options = {
    :bridgeless_enabled => true,
    :fabric_enabled => true,
    :hermes_enabled => false,
  }

  use_test_app! options
  ```

See the full announcement here:
https://reactnative.dev/blog/2023/12/06/0.73-debugging-improvements-stable-symlinks#new-architecture-updates
This commit is contained in:
Tommy Nguyen 2024-02-01 14:07:27 +01:00 коммит произвёл GitHub
Родитель 00ed3eb90b
Коммит 585d29f442
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
21 изменённых файлов: 200 добавлений и 29 удалений

Просмотреть файл

@ -55,4 +55,4 @@ DEPENDENCIES
xcodeproj
BUNDLED WITH
2.4.10
2.5.4

Просмотреть файл

@ -118,6 +118,7 @@ android {
buildConfigField "String", "ReactTestApp_singleApp", singleApp ? "\"${singleApp}\"" : "null"
buildConfigField "boolean", "ReactTestApp_useFabric", enableNewArchitecture.toString()
buildConfigField "boolean", "ReactTestApp_useBridgeless", enableBridgeless.toString()
resValue "string", "app_name", project.ext.react.appName
@ -206,7 +207,11 @@ android {
main.java.srcDirs += [
project.ext.react.enableCamera ? "src/camera/java" : "src/no-camera/java",
enableNewArchitecture ? "src/new-arch/java" : "src/old-arch/java",
!enableNewArchitecture
? "src/old-arch/java"
: enableBridgeless
? "src/new-arch-0.73/java"
: "src/new-arch/java",
// TODO: Remove this block when we drop support for 0.67
// https://github.com/facebook/react-native/commit/ce74aa4ed335d4c36ce722d47937b582045e05c4

Просмотреть файл

@ -0,0 +1,30 @@
package com.microsoft.reacttestapp.compat
import android.app.Application
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import com.microsoft.reacttestapp.BuildConfig
abstract class ReactNativeHostCompat(application: Application) : DefaultReactNativeHost(
application
) {
companion object {
init {
try {
DefaultNewArchitectureEntryPoint.load(
turboModulesEnabled = BuildConfig.ReactTestApp_useFabric,
fabricEnabled = BuildConfig.ReactTestApp_useFabric,
bridgelessEnabled = BuildConfig.ReactTestApp_useBridgeless
)
} catch (e: UnsatisfiedLinkError) {
// Older versions of `DefaultNewArchitectureEntryPoint` is
// hard coded to load `libappmodules.so`
}
SoLoader.loadLibrary("reacttestapp_appmodules")
}
}
override val isNewArchEnabled: Boolean = BuildConfig.ReactTestApp_useFabric
override val isHermesEnabled: Boolean? = true
}

Просмотреть файл

@ -0,0 +1,36 @@
package com.microsoft.reacttestapp.fabric
import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.fabric.ComponentFactory
import com.facebook.soloader.SoLoader
/**
* The corresponding C++ implementation is in `android/app/src/main/jni/ComponentsRegistry.cpp`
*/
@DoNotStrip
class ComponentsRegistry @DoNotStrip private constructor(
componentFactory: ComponentFactory
) {
companion object {
@DoNotStrip
fun register(componentFactory: ComponentFactory): ComponentsRegistry {
return ComponentsRegistry(componentFactory)
}
init {
SoLoader.loadLibrary("fabricjni")
SoLoader.loadLibrary("reacttestapp_appmodules")
}
}
@DoNotStrip
private val mHybridData: HybridData
@DoNotStrip
private external fun initHybrid(componentFactory: ComponentFactory): HybridData
init {
mHybridData = initHybrid(componentFactory)
}
}

Просмотреть файл

@ -0,0 +1,39 @@
package com.microsoft.reacttestapp.turbomodule
import com.facebook.jni.HybridData
import com.facebook.react.ReactPackage
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
import com.facebook.react.bridge.ReactApplicationContext
/**
* These type aliases are here to prevent `@react-native-community/cli` from
* marking them as native modules to autolink.
*
* See also `matchClassName` in
* https://github.com/react-native-community/cli/blob/8.x/packages/platform-android/src/config/findPackageClassName.ts#L25
*/
typealias PackagesList = List<ReactPackage?>
typealias ReactTurboModuleManagerDelegate = ReactPackageTurboModuleManagerDelegate
typealias ReactTurboModuleManagerDelegateBuilder = ReactPackageTurboModuleManagerDelegate.Builder
/**
* The corresponding C++ implementation is in `android/app/src/main/jni/TurboModuleManagerDelegate.cpp`
*/
class TurboModuleManagerDelegate protected constructor(
reactApplicationContext: ReactApplicationContext?,
packages: PackagesList?
) : ReactTurboModuleManagerDelegate(reactApplicationContext, packages) {
external override fun initHybrid(): HybridData?
external fun canCreateTurboModule(moduleName: String?): Boolean
class Builder : ReactTurboModuleManagerDelegateBuilder() {
override fun build(
context: ReactApplicationContext?,
packages: PackagesList?
): TurboModuleManagerDelegate {
return TurboModuleManagerDelegate(context, packages)
}
}
}

Просмотреть файл

@ -72,6 +72,7 @@ ext {
reactNativeVersion = getPackageVersionNumber("react-native", rootDir)
autodetectReactNativeVersion = reactNativeVersion == 0 || reactNativeVersion >= v(0, 71, 0)
enableNewArchitecture = isNewArchitectureEnabled(project)
enableBridgeless = isBridgelessEnabled(project, enableNewArchitecture)
usePrefabs = reactNativeVersion == 0 || reactNativeVersion >= v(0, 71, 0)
androidPluginVersion = getDefaultGradlePluginVersion(reactNativeVersion)

Просмотреть файл

@ -243,6 +243,26 @@ ext.getVersionName = {
return "1.0"
}
ext.isBridgelessEnabled = { project, isNewArchEnabled ->
if (isNewArchEnabled) {
def reactBridgelessEnabled = tryGetProperty(project, "react.bridgelessEnabled")
def bridgelessEnabled = tryGetProperty(project, "bridgelessEnabled")
if (reactBridgelessEnabled == "true" || bridgelessEnabled == "true") {
def version = getPackageVersionNumber("react-native", project.rootDir)
def isSupported = version == 0 || version >= v(0, 73, 0)
if (!isSupported) {
logger.warn([
"react-native 0.73 or greater is required for Bridgeless",
"Mode — disable `bridgelessEnabled` in your",
"`gradle.properties` or upgrade `react-native`"
].join(" "))
}
return isSupported
}
}
return false
}
ext.isFabricEnabled = { project ->
return isNewArchitectureEnabled(project)
}

Просмотреть файл

@ -88,7 +88,7 @@ function testID(label: string): string {
}
function useIsFabricComponent() {
const [isFabric, setIsFabric] = useState(false);
const [isFabric, setIsFabric] = useState(isBridgeless());
const setter = useCallback(
({ currentTarget }: NativeSyntheticEvent<unknown>) => {
setIsFabric(isFabricInstance(currentTarget));
@ -210,6 +210,8 @@ export function App({
<Feature value={isOnOrOff(isFabric && concurrentRoot)}>
Concurrent React
</Feature>
<Separator />
<Feature value={isOnOrOff(isBridgeless())}>Bridgeless</Feature>
</View>
</ScrollView>
</SafeAreaView>

Просмотреть файл

@ -41,6 +41,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# are providing them.
# Note that this is incompatible with web debugging.
#newArchEnabled=true
#bridgelessEnabled=true
# Uncomment the line below if building react-native from source
#ANDROID_NDK_VERSION=26.1.10909125

Просмотреть файл

@ -3,6 +3,7 @@ require_relative '../node_modules/react-native-test-app/test_app'
workspace 'Example.xcworkspace'
options = {
:bridgeless_enabled => false,
:fabric_enabled => false,
:hermes_enabled => false,
}

Просмотреть файл

@ -1020,7 +1020,7 @@ PODS:
- React-jsi (= 0.73.2)
- React-logger (= 0.73.2)
- React-perflogger (= 0.73.2)
- ReactNativeHost (0.4.0):
- ReactNativeHost (0.4.2):
- React-Core
- React-cxxreact
- ReactCommon/turbomodule/core
@ -1248,12 +1248,12 @@ SPEC CHECKSUMS:
React-runtimescheduler: 6517c0cdfae3ea29b599759e069ae97746163248
React-utils: f5bc61e7ea3325c0732ae2d755f4441940163b85
ReactCommon: a42e89b49d88c3890dfb6fd98a33ac80d555be19
ReactNativeHost: e17481b59ed2f3a1c65db1e83f03c1abf9afcd14
ReactNativeHost: 87c7181cb3b0ce016e094ede3cc03b4f61c97dd5
ReactTestApp-DevSupport: 684f374b53c868cd08a0337839a7fcc5ce1e25aa
ReactTestApp-Resources: da77347b3f02b5d79ba3fecb3ad328b2f6a7ef4d
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Yoga: 13c8ef87792450193e117976337b8527b49e8c03
PODFILE CHECKSUM: 66243ee27b434ad0f296253272b27b383190853c
PODFILE CHECKSUM: 0f9a8fd4217f943391e46a62ec8cad034e331d4f
COCOAPODS: 1.14.2

Просмотреть файл

@ -1018,7 +1018,7 @@ PODS:
- React-jsi (= 0.73.6)
- React-logger (= 0.73.6)
- React-perflogger (= 0.73.6)
- ReactNativeHost (0.4.0):
- ReactNativeHost (0.4.2):
- React-Core
- React-cxxreact
- ReactCommon/turbomodule/core
@ -1242,7 +1242,7 @@ SPEC CHECKSUMS:
React-runtimescheduler: 06ca3657c857fb14d00ec548c489f3b8c552601e
React-utils: 68f5b33b6957324718290e3c7eda822db1da9b56
ReactCommon: e80b030b22caf9def0e375bb5a46c02305b95e10
ReactNativeHost: e17481b59ed2f3a1c65db1e83f03c1abf9afcd14
ReactNativeHost: 87c7181cb3b0ce016e094ede3cc03b4f61c97dd5
ReactTestApp-DevSupport: 684f374b53c868cd08a0337839a7fcc5ce1e25aa
ReactTestApp-Resources: 8539dac0f8d2ef3821827a537e37812104c6ff78
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d

Просмотреть файл

@ -45,7 +45,7 @@
"kitType": "app",
"alignDeps": {
"requirements": [
"react-native@0.72"
"react-native@0.73"
],
"capabilities": [
"core-android",

Просмотреть файл

@ -27,10 +27,6 @@ final class ReactInstance: NSObject, RNXHostConfig {
super.init()
#if USE_FABRIC
RCTEnableTurboModule(true)
#endif
NotificationCenter.default.addObserver(
self,
selector: #selector(onJavaScriptLoaded(_:)),

Просмотреть файл

@ -1,3 +1,10 @@
def bridgeless_enabled?(options, react_native_version)
return false unless new_architecture_enabled?(options, react_native_version)
supports_bridgeless = react_native_version.zero? || react_native_version >= v(0, 73, 0)
supports_bridgeless && options[:bridgeless_enabled]
end
def find_file(file_name, current_dir)
return if current_dir.expand_path.to_s == '/'
@ -40,9 +47,8 @@ def try_pod(name, podspec, project_root)
pod name, :podspec => podspec if File.exist?(File.join(project_root, podspec))
end
def use_new_architecture!(options)
new_arch_enabled = new_architecture_enabled?(options, v(1_000, 0, 0))
return unless new_arch_enabled
def use_new_architecture!(options, react_native_version)
return unless new_architecture_enabled?(options, react_native_version)
Pod::UI.warn(
'As of writing, New Architecture (Fabric) is still experimental and ' \
@ -53,6 +59,7 @@ def use_new_architecture!(options)
options[:fabric_enabled] = true
options[:new_arch_enabled] = true
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
ENV['USE_BRIDGELESS'] = '1' if bridgeless_enabled?(options, react_native_version)
end
def v(major, minor, patch)

Просмотреть файл

@ -198,7 +198,8 @@ def use_react_native!(project_root, target_platform, options)
include_react_native!(**options,
app_path: find_file('package.json', project_root).parent.to_s,
path: react_native.relative_path_from(project_root).to_s,
rta_project_root: project_root)
rta_project_root: project_root,
version: version)
end
def make_project!(xcodeproj, project_root, target_platform, options)
@ -304,6 +305,7 @@ def make_project!(xcodeproj, project_root, target_platform, options)
build_settings['PRODUCT_BUILD_NUMBER'] = build_number || '1'
use_new_arch = new_architecture_enabled?(options, rn_version)
use_bridgeless = bridgeless_enabled?(options, rn_version)
app_project = Xcodeproj::Project.open(xcodeproj_dst)
app_project.native_targets.each do |target|
case target.name
@ -327,6 +329,9 @@ def make_project!(xcodeproj, project_root, target_platform, options)
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'FOLLY_NO_CONFIG=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'RCT_NEW_ARCH_ENABLED=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'USE_FABRIC=1'
if use_bridgeless
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'USE_BRIDGELESS=1'
end
end
build_settings.each do |setting, value|
@ -335,6 +340,7 @@ def make_project!(xcodeproj, project_root, target_platform, options)
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)']
config.build_settings['OTHER_SWIFT_FLAGS'] << '-DUSE_FABRIC' if use_new_arch
config.build_settings['OTHER_SWIFT_FLAGS'] << '-DUSE_BRIDGELESS' if use_bridgeless
if single_app.is_a? String
config.build_settings['OTHER_SWIFT_FLAGS'] << '-DENABLE_SINGLE_APP_MODE'
end

Просмотреть файл

@ -3,11 +3,11 @@ require 'open3'
require_relative('pod_helpers')
def include_react_native!(options)
react_native, project_root = options.values_at(:path, :rta_project_root)
react_native, project_root, version = options.values_at(:path, :rta_project_root, :version)
require_relative(File.join(project_root, react_native, 'scripts', 'react_native_pods'))
use_new_architecture!(options)
use_new_architecture!(options, version)
use_react_native!(
path: react_native,
fabric_enabled: options[:fabric_enabled] == true,

Просмотреть файл

@ -80,7 +80,7 @@
"test:rb": "bundle exec ruby -Ilib:test -e \"Dir.glob('./test/test_*.rb').each { |file| require(file) }\""
},
"dependencies": {
"@rnx-kit/react-native-host": "^0.4.0",
"@rnx-kit/react-native-host": "^0.4.2",
"ajv": "^8.0.0",
"chalk": "^4.1.0",
"cliui": "^8.0.0",

Просмотреть файл

@ -77,6 +77,9 @@ describe("npm pack", () => {
"android/app/src/main/res/values/colors.xml",
"android/app/src/main/res/values/strings.xml",
"android/app/src/main/res/values/styles.xml",
"android/app/src/new-arch-0.73/java/com/microsoft/reacttestapp/compat/ReactNativeHostCompat.kt",
"android/app/src/new-arch-0.73/java/com/microsoft/reacttestapp/fabric/ComponentsRegistry.kt",
"android/app/src/new-arch-0.73/java/com/microsoft/reacttestapp/turbomodule/TurboModuleManagerDelegate.kt",
"android/app/src/new-arch/java/com/microsoft/reacttestapp/compat/ReactNativeHostCompat.kt",
"android/app/src/new-arch/java/com/microsoft/reacttestapp/fabric/ComponentsRegistry.kt",
"android/app/src/new-arch/java/com/microsoft/reacttestapp/turbomodule/TurboModuleManagerDelegate.kt",

Просмотреть файл

@ -3,25 +3,49 @@ require('minitest/autorun')
require_relative('../ios/pod_helpers')
class TestPodHelpers < Minitest::Test
def test_bridgeless_enabled?
ENV.delete('RCT_NEW_ARCH_ENABLED')
# Bridgeless mode is first publicly available in 0.73
available_version = v(0, 73, 0)
refute(bridgeless_enabled?({}, 0))
refute(bridgeless_enabled?({}, available_version))
options = { :bridgeless_enabled => true, :fabric_enabled => true }
refute(bridgeless_enabled?(options, v(0, 72, 999)))
assert(bridgeless_enabled?(options, available_version))
# `RCT_NEW_ARCH_ENABLED` does not enable bridgeless
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
refute(bridgeless_enabled?({}, v(0, 72, 999)))
refute(bridgeless_enabled?({}, available_version))
end
def test_new_architecture_enabled?
ENV.delete('RCT_NEW_ARCH_ENABLED')
# New architecture is first publicly available in 0.68, but we'll require 0.71
available_version = v(0, 71, 0)
refute(new_architecture_enabled?({}, 0))
refute(new_architecture_enabled?({}, v(0, 71, 0)))
refute(new_architecture_enabled?({}, available_version))
# New architecture is first publicly available in 0.68, but we'll require 0.71
refute(new_architecture_enabled?({ :fabric_enabled => true }, v(0, 70, 999)))
assert(new_architecture_enabled?({ :fabric_enabled => true }, v(0, 71, 0)))
assert(new_architecture_enabled?({ :fabric_enabled => true }, available_version))
# TODO: `:turbomodule_enabled` is scheduled for removal in 4.0
refute(new_architecture_enabled?({ :turbomodule_enabled => true }, v(0, 70, 999)))
assert(new_architecture_enabled?({ :turbomodule_enabled => true }, v(0, 71, 0)))
assert(new_architecture_enabled?({ :turbomodule_enabled => true }, available_version))
# `RCT_NEW_ARCH_ENABLED` enables everything
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
refute(new_architecture_enabled?({}, v(0, 70, 999)))
assert(new_architecture_enabled?({}, v(0, 71, 0)))
assert(new_architecture_enabled?({}, available_version))
end
def test_v

Просмотреть файл

@ -3292,12 +3292,12 @@ __metadata:
languageName: node
linkType: hard
"@rnx-kit/react-native-host@npm:^0.4.0":
version: 0.4.0
resolution: "@rnx-kit/react-native-host@npm:0.4.0"
"@rnx-kit/react-native-host@npm:^0.4.2":
version: 0.4.2
resolution: "@rnx-kit/react-native-host@npm:0.4.2"
peerDependencies:
react-native: ">=0.66"
checksum: 7a03ee90caacf6f015e1c720ca8e1b50ff7af866265d01079ee4ef0f9689884e7839dd83940ffa97462232e5913f152a3a17b3975dd7b1aebb83f7a3ac88dae4
checksum: 691a7b66791d976c07036f67c95947ee7828dfe63c6d8a94edfda8dfb08b87795dd0762df4f96032e668c71114eb4608090bb445f0d53f0744667b31604d2432
languageName: node
linkType: hard
@ -12087,7 +12087,7 @@ __metadata:
"@microsoft/eslint-plugin-sdl": "npm:^0.2.0"
"@react-native-community/cli": "npm:^12.3.0"
"@rnx-kit/eslint-plugin": "npm:^0.6.0"
"@rnx-kit/react-native-host": "npm:^0.4.0"
"@rnx-kit/react-native-host": "npm:^0.4.2"
"@rnx-kit/tsconfig": "npm:^1.0.0"
"@types/js-yaml": "npm:^4.0.5"
"@types/mustache": "npm:^4.0.0"