diff --git a/docs/ANDROID.md b/docs/ANDROID.md index d946cf29..465ed7ed 100644 --- a/docs/ANDROID.md +++ b/docs/ANDROID.md @@ -4,6 +4,35 @@ Because we're using a version of Hermes patched with Node-API support, we need to build React Native from source. +Follow [the React Native documentation on how to build from source](https://reactnative.dev/contributing/how-to-build-from-source#update-your-project-to-build-from-source). + +In particular, you will have to edit the `android/settings.gradle` file as follows: + +> ```diff +> // ... +> include ':app' +> includeBuild('../node_modules/@react-native/gradle-plugin') +> +> + includeBuild('../node_modules/react-native') { +> + dependencySubstitution { +> + substitute(module("com.facebook.react:react-android")).using(project(":packages:react-native:ReactAndroid")) +> + substitute(module("com.facebook.react:react-native")).using(project(":packages:react-native:ReactAndroid")) +> + substitute(module("com.facebook.react:hermes-android")).using(project(":packages:react-native:ReactAndroid:hermes-engine")) +> + substitute(module("com.facebook.react:hermes-engine")).using(project(":packages:react-native:ReactAndroid:hermes-engine")) +> + } +> + } +> ``` + +To download our custom version of Hermes, you need to run from your app package: + +``` +npx react-native-node-api vendor-hermes +``` + +This will print a path which needs to be stored in `REACT_NATIVE_OVERRIDE_HERMES_DIR` to instruct the React Native Gradle scripts to use it. + +This can be combined into a single line: + ``` export REACT_NATIVE_OVERRIDE_HERMES_DIR=`npx react-native-node-api vendor-hermes --silent` ``` diff --git a/docs/HOW-IT-WORKS.md b/docs/HOW-IT-WORKS.md index f35762c8..cd4ae854 100644 --- a/docs/HOW-IT-WORKS.md +++ b/docs/HOW-IT-WORKS.md @@ -33,14 +33,6 @@ module.exports = require("react-native-node-api").requireNodeAddon( > In the time of writing, this code only supports iOS as passes the path to the library with its .framework. > We plan on generalizing this soon 🤞 -### A note on the need for path-hashing - -Notice that the `requireNodeAddon` call doesn't reference the library by it's original name (`prebuild.node`) but instead a name containing a hash. - -In Node.js dynamic libraries sharing names can be disambiguated based off their path on disk. Dynamic libraries added to an iOS application are essentially hoisted and occupy a shared global namespace. This leads to collisions and makes it impossible to disambiguate multiple libraries sharing the same name. We need a way to map a require call, referencing the library by its path relative to the JS file, into a unique name of the library once it's added into the application. - -To work around this issue, we scan for and copy any library (including its entire xcframework structure with nested framework directories) from the dependency package into our host package when the app builds and reference these from its podspec (as vendored_frameworks). We use a special file in the xcframeworks containing Node-API modules. To avoid collisions we rename xcframework, framework and library files to a unique name, containing a hash. The hash is computed based off the package-name of the containing package and the relative path from the package root to the library file (with any platform specific file extensions replaced with the neutral ".node" extension). - ## Transformed code calls into `react-native-node-api`, loading the platform specific dynamic library The native implementation of `requireNodeAddon` is responsible for loading the dynamic library and allow the Node-API module to register its initialization function, either by exporting a `napi_register_module_v1` function or by calling the (deprecated) `napi_module_register` function. diff --git a/docs/USAGE.md b/docs/USAGE.md index e09cf4d1..9d8e53b8 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -17,16 +17,15 @@ The app developer has to install both `calculator-lib` as well as `react-native- The reason for the latter is a current limitation of the React Native Community CLI which doesn't consider transitive dependencies when enumerating packages for auto-linking. > [!WARNING] -> It's important to match the exact version of the `react-native-node-api` declared as peer dependency by `calculator-lib`. +> It's important to match the version range of the `react-native-node-api` declared as a peer dependency by `calculator-lib`. -For the app to resolve the Node-API dynamic library files, the app developer must update their Metro config to use a `resolveRequest` function exported from `react-native-node-api`: +For the app to resolve the Node-API dynamic library files, the app developer must update their Babel config to use a `requireNodeAddon` function exported from `react-native-node-api`: ```javascript -const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); -const nodeApi = require("react-native-node-api/metro-config"); -module.exports = mergeConfig(getDefaultConfig(__dirname), { - resolver: { resolveRequest: nodeApi.resolveRequest }, -}); +module.exports = { + presets: ["module:@react-native/babel-preset"], + plugins: ["module:react-native-node-api/babel-plugin"], // 👈 This needs to be added to the babel.config.js of the app +}; ``` At some point the app code will import (or require) the entrypoint of `calculator-lib`: @@ -121,18 +120,36 @@ NAPI_MODULE_INIT(/* napi_env env, napi_value exports */) { } ``` -### Build the prebuilt binaries +```cmake +# CMakeLists.txt -``` -npx react-native-node-api build ./addon.c +cmake_minimum_required(VERSION 3.15...3.31) +project(addon) + +add_compile_definitions(-DNAPI_VERSION=4) + +file(GLOB SOURCE_FILES "addon.c") + +add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_JS_INC}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_JS_LIB}) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + +if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) + # Generate node.lib + execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) +endif() ``` -This is a shorthand command which generates a CMake project from the single source-file and prebuilds for both the Apple and Android platforms. See the [CLI documentation](./CLI.md) for more information on the options available and [documentation on prebuilds](./PREBUILDS.md) for the specifics on their format and structure. +### Build the prebuilt binaries - +``` +npx cmake-rn +``` ### Load and export the native module ```javascript -module.exports = require("./prebuild.node"); +module.exports = require("./build/Release/addon.node"); ```