RSPACK_BINDING is ignored by parallel-loader workers, loading two native bindings in one process
The complete reproduction/attribution note is also published in the HMR handoff gist (available privately).
While validating an optimized Rspack HMR binding in a large Rsbuild app, we exported:
RSPACK_BINDING=/tmp/rspack-leaf-fix/crates/node_binding/binding.js
The main Rspack compiler correctly loads the custom native binding, but the loader-worker bundle still loads the installed binding. /proc/<pid>/maps confirms both .node libraries are resident in the same Node process:
/tmp/rspack-leaf-fix/crates/node_binding/rspack.linux-x64-gnu.node
sha256 43aeb4bb914277fba080227d85de59fd45c73f329b393ef05be87d81e3ff2418
.../node_modules/.pnpm/@rspack+binding-linux-x64-gnu@2.1.3/.../rspack.linux-x64-gnu.node
sha256 82b9d4504122453a4cc5f7dd926b97bc44907c89d1e5923cb65b148c316f6ab0
@rspack/core/dist/index.js honors the override at both binding-load sites:
let binding_namespaceObject = __rspack_createRequire_require(
process.env.RSPACK_BINDING ? process.env.RSPACK_BINDING : "@rspack/binding"
);
but @rspack/core/dist/worker.js:17 is hard-coded:
let binding_namespaceObject = __rspack_createRequire_require("@rspack/binding");
That worker is used by the Tinypool parallel-loader service (dist/index.js, ensureLoaderWorkerPool, which resolves worker.js). It uses the binding for loader state and SWC transform/minify, so custom-binding tests can unknowingly run the compiler and loader workers against different native builds. This also makes version/API changes risky and adds avoidable resident code/data.
The cause is in packages/rspack/rslib.config.ts: codmodPlugin defines replaceBinding, but onAfterBuild reads and rewrites only dist/index.js. A small upstream fix is to apply the same rewrite to both dist/index.js and dist/worker.js (the worker contains pattern 1, while index contains patterns 1 and 2), with a regression asserting that both outputs reference process.env.RSPACK_BINDING.
For reproducible native A/B testing today, overlay the platform .node file after backing it up. Avoid NAPI_RS_NATIVE_LIBRARY_PATH in a full application: it is global to napi-rs and causes unrelated native packages such as Tailwind Oxide or Rolldown to load the Rspack library.
RSPACK_BINDINGis ignored by parallel-loader workers, loading two native bindings in one processThe complete reproduction/attribution note is also published in the HMR handoff gist (available privately).
While validating an optimized Rspack HMR binding in a large Rsbuild app, we exported:
The main Rspack compiler correctly loads the custom native binding, but the loader-worker bundle still loads the installed binding.
/proc/<pid>/mapsconfirms both.nodelibraries are resident in the same Node process:@rspack/core/dist/index.jshonors the override at both binding-load sites:but
@rspack/core/dist/worker.js:17is hard-coded:That worker is used by the Tinypool parallel-loader service (
dist/index.js,ensureLoaderWorkerPool, which resolvesworker.js). It uses the binding for loader state and SWC transform/minify, so custom-binding tests can unknowingly run the compiler and loader workers against different native builds. This also makes version/API changes risky and adds avoidable resident code/data.The cause is in
packages/rspack/rslib.config.ts:codmodPlugindefinesreplaceBinding, butonAfterBuildreads and rewrites onlydist/index.js. A small upstream fix is to apply the same rewrite to bothdist/index.jsanddist/worker.js(the worker contains pattern 1, while index contains patterns 1 and 2), with a regression asserting that both outputs referenceprocess.env.RSPACK_BINDING.For reproducible native A/B testing today, overlay the platform
.nodefile after backing it up. AvoidNAPI_RS_NATIVE_LIBRARY_PATHin a full application: it is global to napi-rs and causes unrelated native packages such as Tailwind Oxide or Rolldown to load the Rspack library.