Fork of
@react-native-community/javascriptcorewith fixes for React Native 0.84+,use_frameworks! :linkage => :static, and New Architecture.
This fork rewrites the iOS podspec to work with RN 0.84's module system and fixes header resolution issues that occur when using use_frameworks!.
JavaScriptCore was extracted from core react-native as part of the Lean Core JSC RFC (PR).
The following issues exist in React Native core and require patches (see Required Patches below):
| Issue | Description | PR |
|---|---|---|
| #54268 | Hermes pods still installed when USE_THIRD_PARTY_JSC=1 |
#55817 |
| Compile error | createJSRuntimeFactory missing #else branch |
#55817 |
npm install github:crisposoft/javascriptcore
# or
yarn add github:crisposoft/javascriptcoreAlso install patch-package if you don't have it already:
npm install --save-dev patch-packageAdd these environment variables at the top of your ios/Podfile, before any target blocks:
# Use JSC instead of Hermes
ENV['USE_THIRD_PARTY_JSC'] = '1'
ENV['USE_HERMES'] = '0'
# Force building RN dependencies from source (required for JSC compatibility)
ENV['RCT_USE_RN_DEP'] = '0'Why
RCT_USE_RN_DEP=0? RN 0.84 defaults to prebuiltReactNativeDependenciesxcframework which bundles Folly, DoubleConversion, glog, and boost. Building from source avoids conflicts with the JSC pod.
React Native 0.84 has several bugs that prevent JSC from working. Create patches/react-native+0.84.1.patch (adjust version to match yours) with the following content:
Click to expand full patch
diff --git a/node_modules/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm b/node_modules/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm
index 3b917c1..3960753 100644
--- a/node_modules/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm
+++ b/node_modules/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm
@@ -45,6 +45,10 @@ - (JSRuntimeFactoryRef)createJSRuntimeFactory
{
#if USE_THIRD_PARTY_JSC != 1
return jsrt_create_hermes_factory();
+#else
+ [NSException raise:@"JSRuntimeFactory"
+ format:@"createJSRuntimeFactory must be overridden when using third-party JSC"];
+ return nil;
#endif
}
diff --git a/node_modules/react-native/scripts/react_native_pods.rb b/node_modules/react-native/scripts/react_native_pods.rb
index 231f420..0679f34 100644
--- a/node_modules/react-native/scripts/react_native_pods.rb
+++ b/node_modules/react-native/scripts/react_native_pods.rb
@@ -75,7 +75,7 @@ def use_react_native! (
error_if_try_to_use_jsc_from_core()
warn_if_new_arch_disabled()
- hermes_enabled= true
+ hermes_enabled= !use_third_party_jsc()
# Set the app_path as env variable so the podspecs can access it.
ENV['APP_PATH'] = app_path
ENV['REACT_NATIVE_PATH'] = path-
RCTDefaultReactNativeFactoryDelegate.mm—createJSRuntimeFactory: Adds an#elsebranch so the method has a return value whenUSE_THIRD_PARTY_JSC=1. Without this, the build fails with a "non-void function does not return a value" error. -
react_native_pods.rb—hermes_enabled: Thehermes_enabledflag is hardcoded totrue, ignoringUSE_THIRD_PARTY_JSC. This causeshermes-engine,React-hermes, andReact-RuntimeHermespods to always be installed. The patch setshermes_enabled = !use_third_party_jsc()so Hermes pods are excluded when JSC is configured.
After creating the patch file, make sure your package.json has a postinstall script:
{
"scripts": {
"postinstall": "patch-package"
}
}Add these compiler flags in your post_install block so that C++ preprocessor guards throughout React Native correctly detect the JSC configuration:
post_install do |installer|
react_native_post_install(installer, "../node_modules/react-native")
# Inject JSC/Hermes preprocessor flags at the Xcode project level
if use_third_party_jsc()
ReactNativePodsUtils.add_compiler_flag_to_project(installer, "-DUSE_THIRD_PARTY_JSC=1")
ReactNativePodsUtils.add_compiler_flag_to_project(installer, "-DUSE_HERMES=0")
end
endWhy is this needed?
RCTCxxBridge.mmand other RN source files check#if !defined(USE_HERMES)or#if USE_THIRD_PARTY_JSC. The podspec-leveljs_engine_flags()is dead code in RN 0.84, so these flags must be injected at the project level.
#import <ReactJSC/RCTJscInstanceFactory.h>
// Inside your AppDelegate implementation:
- (JSRuntimeFactoryRef)createJSRuntimeFactory
{
return jsrt_create_jsc_factory();
}import ReactJSC
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
override func createJSRuntimeFactory() -> JSRuntimeFactoryRef {
jsrt_create_jsc_factory()
}
}cd ios && pod installYou should see that hermes-engine, React-hermes, and React-RuntimeHermes are not in the installed pods. The total pod count should be ~3 fewer than with Hermes.
ENV['USE_THIRD_PARTY_JSC'] = '1'
ENV['USE_HERMES'] = '0'
ENV['RCT_USE_RN_DEP'] = '0'
def node_require(script)
require Pod::Executable.execute_command('node', ['-p',
"require.resolve('#{script}', {paths: [process.argv[1]]})", __dir__]).strip
end
node_require('react-native/scripts/react_native_pods.rb')
platform :ios, min_ios_version_supported
prepare_react_native_project!
use_frameworks! :linkage => :static
target 'MyApp' do
config = use_native_modules!
use_react_native!(
:path => config[:reactNativePath],
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
end
post_install do |installer|
react_native_post_install(installer, "../node_modules/react-native")
if use_third_party_jsc()
ReactNativePodsUtils.add_compiler_flag_to_project(installer, "-DUSE_THIRD_PARTY_JSC=1")
ReactNativePodsUtils.add_compiler_flag_to_project(installer, "-DUSE_HERMES=0")
end
endhermesEnabled=false
useThirdPartyJSC=true+import io.github.reactnativecommunity.javascriptcore.JSCExecutorFactory
+import io.github.reactnativecommunity.javascriptcore.JSCRuntimeFactory
+import com.facebook.react.bridge.JavaScriptExecutorFactory
+import com.facebook.react.modules.systeminfo.AndroidInfoHelpers
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
// ...
+ override fun getJavaScriptExecutorFactory(): JavaScriptExecutorFactory =
+ JSCExecutorFactory(packageName, AndroidInfoHelpers.getFriendlyDeviceName())
}
+ override val reactHost: ReactHost
+ get() = getDefaultReactHost(applicationContext, reactNativeHost, JSCRuntimeFactory())
}This fork (crisposoft/javascriptcore) includes the following changes over the upstream:
- Podspec rewrite: Uses
add_dependencyhelper with correct framework names for all transitive dependencies, fixing header resolution withuse_frameworks! - Non-modular header fix: Forward-declares
JSRuntimeFactoryRefin the public header instead of importing the non-modular<react/runtime/JSRuntimeFactoryCAPI.h>, avoiding "include of non-modular header inside framework module" errors package.jsonfix: Correctedfilesfield from["ios", ...]to["apple", ...]to match the actual directory structure
Special thanks to the team who worked on the initial extraction of JavaScriptCore from core react-native:
Everything inside this repository is MIT licensed.