@react-native-gpui/hermes

中文文档
0.87.0-alpha.0发布于 2026/11/02MIT零依赖

Pinned Hermes engine for react-native-gpui: public headers and prebuilt VM and compiler binaries

原始使用文档
文档目录9 节
  1. Install
  2. Build
  3. Layout
  4. Node-API addons
  5. Windows
  6. Fetching the binaries
  7. Building it yourself
  8. Intl
  9. License

@react-native-gpui/hermes

The pinned Hermes engine for react-native-gpui, packaged the way React Native ships its own engine: the public headers we compile against, plus the prebuilt VM and hermesc compiler for every platform we support.

Part of react-native-gpui, React Native for macOS, Windows and Linux.

Install

Apps created with npx @react-native-gpui/cli init already depend on it. In an existing app:

npm install @react-native-gpui/hermes

The engine binaries for your platform are downloaded from the matching GitHub release the first time the app is built.

The rest of this file describes how the package is built and maintained.

Build

Everything here is built from one commit, recorded in package.json:

"hermes": {
  "repository": "https://github.com/facebook/hermes",
  "commit": "10a40b5f1de81d007cba6afe567c4ba7a0b45f5d"
}

Layout

PathContents
API/, public/, include/The upstream headers we compile against, byte for byte (below)
bin/<platform>-<arch>/The prebuilt VM and compiler (not committed; fetched or built)
desktop/node-api-exports.txtEvery napi_*/node_api_* name the VM exports, one per line
patches/Patches applied to the pinned commit before building
scripts/fetch-binaries.mjsDownloads a CI artifact or release into bin/ and verifies it

desktop/node-api-exports.txt lists the Node-API names the VM exports. On Windows an addon cannot resolve those symbols from a global scope — a DLL import names the file it comes from — so the app's executable re-exports each one, forwarded to hermesvm.dll. The list is what that forwarding is generated from, and desktop/node-api-exports.test.js regenerates it from the installed library and fails when the two disagree. Regenerate it after a Hermes bump:

dumpbin /exports bin/win32-x64/hermesvm.dll | # keep the napi_* / node_api_* rows

Only the headers needed to compile against the VM are vendored. Everything else in upstream include/ is VM internals the shared library does not export.

  • API/{hermes,jsi,napi,hermes_abi}/**/*.h: every upstream header in those folders (hermes.h, the debugger and CDP headers React Native's inspector uses, hermes_napi.h, JSI and the Hermes ABI). Sources and tooling under API/ and the unused API/hermes_sandbox are not vendored.
  • public/hermes/Public/*.h: config and export headers that hermes.h includes.
  • include/hermes/napi/: the Node-API C headers that hermes_napi.h includes, with upstream's VENDORED.md.

The build compiles against React Native's JSI (ReactCommon/jsi); API/jsi is kept only so the API folder matches upstream.

Node-API addons

The engine owns the Node-API half of loading a stock .node file. The build enables HERMES_ENABLE_NAPI, so the VM implements Node-API itself: libhermesvm exports the napi_* and node_api_* symbols, and an addon links against nothing — its imports resolve against whatever already loaded the VM.

desktop/HermesNodeAddons.cpp creates one Node-API environment per addon through hermes_napi_create_env and loads the file with hermes_napi_load_module, returning the exports as a JSI value. desktop/HermesNodeApiHost.cpp supplies the host callbacks that environment needs: post_work, cancel_work, post_task and fatal_exception. post_task and the async-work callbacks hop to the JS thread through the CallInvoker, so core's _requireNodeLibrary global, path resolution, per-runtime cache and error messages stay engine-agnostic.

A missing symbol in the addon surfaces as a dlerror() string, which is what names an addon that needs libuv (uv_default_loop) rather than Node-API.

Windows

Windows has no dlopen and no global symbol scope, so two things differ there and both are handled by patches/0002-windows-load-node-api-modules.patch:

  • the VM loads a .node file with LoadLibraryExW and GetProcAddress instead of dlopen/dlsym, converting the path to UTF-16 and using LOAD_WITH_ALTERED_SEARCH_PATH so an addon that ships a sibling DLL finds it;
  • a failed load raises Failed to load module: <reason>, where <reason> is the system's text for GetLastError() from FormatMessageW. That is the message that tells a user an addon needs uv_*, or was built without node-gyp's delay-load hook (a missing node.exe import).

The patch also hoists InitFunc/initFunc above the #ifdef. Upstream declares them inside the #else and closes the conditional after the shared tail, so on Windows the code creating the exports object, calling the entry point and returning was compiled out; the function fell off the end and returned an indeterminate value, which surfaced as napi_invalid_arg for every addon.

Unlike 0001 this patch is not behind an option: it is what makes the engine able to load addons on Windows at all. scripts/build-hermes-windows.ps1 applies every patch in patches/ before configuring, checking with git apply --check first and reporting the ones it applied, so a build from a clean checkout always matches the pinned commit plus these changes.

bin/ is gitignored, so a fresh clone has the headers but no binaries:

PlatformFiles
darwin-arm64, darwin-x64libhermesvm.dylib, hermesc
linux-x64, linux-arm64libhermesvm.so, hermesc
win32-x64, win32-arm64hermesvm.dll, hermesvm.lib, hermesc.exe

Fetching the binaries

# latest successful hermes-engine workflow run (needs an authenticated gh)
npm run fetch-binaries

# a specific run, a release tag, or an artifact you downloaded by hand
node scripts/fetch-binaries.mjs --run 123456789
node scripts/fetch-binaries.mjs --tag hermes-10a40b5
node scripts/fetch-binaries.mjs --from ~/Downloads/hermes-darwin-arm64.zip

# check what is present: the host platform, or every platform
npm run verify-binaries
node scripts/fetch-binaries.mjs --verify --all

A fetched binary is built from the pinned commit plus every patch in patches/: the workflow applies them before configuring. The local scripts do the same, so the two agree.

Building it yourself

.github/workflows/hermes-engine.yml builds all six targets and uploads one artifact per platform. The same builds run locally through the repo scripts:

scripts/build-hermes-macos.sh      # builds universal, then lipo -thin per arch
scripts/build-hermes-linux.sh      # linux-x64
scripts/build-hermes-windows.ps1   # win32-x64, run from a Developer PowerShell

Each script expects a facebook/hermes checkout at the pinned commit (override with RN_GPUI_HERMES_SOURCE_DIR) and warns when its HEAD does not match.

The build flags are mirrored from package.json's hermes.buildFlags:

HERMES_ENABLE_NAPI=ON
HERMES_ENABLE_DEBUGGER=ON
HERMES_ENABLE_INTL=ON
HERMES_ICU_RUNTIME_LOAD=ON
HERMES_ICU_HEADERS_DIR=packages/hermes/third_party/icu
HERMES_ENABLE_TEST_SUITE=OFF
HERMES_BUILD_APPLE_FRAMEWORK=OFF

Intl

HERMES_ENABLE_INTL=ON exposes Intl.Collator, Intl.NumberFormat and Intl.DateTimeFormat (plus Intl.getCanonicalLocales, formatToParts, and the toLocale* / localeCompare prototype methods). The pinned Hermes has no Intl.PluralRules, ListFormat, RelativeTimeFormat or Segmenter.

Every platform uses one backend (patches/0003-intl-on-the-system-icu.patch) written against ICU's C API, with ICU loaded when it is first needed, so the engine ships no ICU data and links no ICU:

PlatformICU it loadsMinimum
macOS/usr/lib/libicucore.A.dylibthe ICU of the OS (64+)
Windowsicu.dll from System32Windows 10 1903 (ICU 64)
Linuxlibicui18n.so.N, N from 100 down to 64, with libicuucICU 64

RN_GPUI_ICU_VERSION forces one Linux version, and 0 turns loading off so the fallback can be tested. With no usable ICU the VM still starts: it logs one warning, Intl stays defined, numbers format as plain JavaScript numbers, dates as ISO 8601 in UTC, strings compare by code unit, and case mapping covers ASCII only.

The ICU headers it compiles against are vendored in third_party/icu (ICU 76.1, unmodified, for signatures only). The backend calls only functions that ICU 64 has; uloc_openAvailableByType and ucal_getCanonicalTimeZoneID are optional. NumberFormat builds an ICU number skeleton from the resolved options and formats with unumf_*, so style (decimal, currency, percent, unit), digits, grouping, compact and scientific notation, signDisplay, roundingMode and trailingZeroDisplay come from ICU itself.

scripts/intl-smoke.js holds the assertions the hermes-engine workflow runs on every platform, and on Linux against ICU 70, ICU 74 and no ICU.

License

MIT. See LICENSE. Hermes is MIT-licensed by Meta Platforms, Inc.

版本与发布记录

@react-native-gpui/hermes 共发布 2 个版本,首次发布于 2026/11/02;过去 12 个月发布了 2 个版本。

版本发布日期距今
0.87.0-alpha.0latest2026/11/021 天前
0.0.0-stage2026/11/021 天前

关于 @react-native-gpui/hermes 的常见问题

@react-native-gpui/hermes 是什么?

用于react-native-gpui的固定Hermes引擎:公共头文件和预构建的VM和编译器二进制文件

如何安装 @react-native-gpui/hermes?

在项目目录运行 npm install @react-native-gpui/hermes,或使用 pnpm add / yarn add / bun add。

@react-native-gpui/hermes 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/react-native-gpui__hermes 是否存在。

@react-native-gpui/hermes 可以商用吗?

@react-native-gpui/hermes 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@react-native-gpui/hermes 还在维护吗?

最新版本 0.87.0-alpha.0 发布于 2026/11/02(1 天前);至今共发布 2 个版本,其中过去 12 个月发布了 2 个;首次发布于 2026/11/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。