feat(node_binding): compressed @rspack/binding via a self-loading hybrid .node (RFC/draft)#14719
feat(node_binding): compressed @rspack/binding via a self-loading hybrid .node (RFC/draft)#14719jdalton wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This draft/RFC introduces a proposed “hybrid self-loading” compressed format for @rspack/binding (shipping a .node that contains a zstd-compressed payload and integrity hash), along with reference tooling and documentation to validate the section layout and measure size impact.
Changes:
- Add a reference pack/unpack implementation for the
PRESSED_DATAsection payload layout (zstd + SHA-512). - Add a local benchmark script that locates the installed platform binding and reports raw vs zstd-19 size (and compression time).
- Add a short design doc describing the hybrid format and intended runtime behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| crates/node_binding/scripts/pack-compressed-binding.mjs | Reference pack/unpack logic for the pressed-data payload layout (zstd + SHA-512). |
| crates/node_binding/scripts/bench-compressed-binding.mjs | Local benchmark utility to resolve installed binding and report size/compression metrics. |
| crates/node_binding/docs/compressed-binding.md | Design/RFC documentation for the hybrid compressed binding approach and runtime plan. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…rid .node Ship the native addon zstd-compressed inside the .node and unwrap it on first load, handing it to the OS's transparent filesystem compression (APFS / NTFS / btrfs) so it takes fewer blocks on disk and loads faster (the kernel reads fewer bytes), reusing the decmpfs crate — no @napi-rs/cli change. - scripts/pack-compressed-binding.mjs: reference packer (zstd + SHA-512) in the layout decmpfs unwraps; unpack() fails closed on a short, tampered, oversized, or non-zstd section. - scripts/pack-compressed-binding.test.mjs: node:test unit tests (round-trip, integrity, rejection paths), pure in-memory. - scripts/bench-compressed-binding.mjs: resolves whichever @rspack/binding is installed for the platform and reports the download size win. - docs/compressed-binding.md: design doc. Draft/RFC — per-platform section injection and the Rust first-load loader still need rspack's cross-compile CI.
7e3950c to
d6608c6
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
Draft / RFC — checking the direction before finishing the native part.
Today rspack ships
@rspack/binding(the.nodefile) uncompressed. This ships the real.nodezstd-compressed inside the file and, on first load, hands it to the OS's own transparent filesystem compression (APFS / NTFS / btrfs). The kernel then decompresses it on read, so it takes fewer blocks on disk and loads faster — reading fewer bytes beats the cheap kernel decompress. It needs no change to@napi-rs/cli— the loader is rspack's own Rust, reusing thedecmpfscrate..nodedownload is 13.8 MiB (zstd-19, −64%) instead of 38.4 MiB.Related: the napi-rs
--compressproposal, and the same idea shipped in aube — store compression and its benchmark.How it works
The real
.nodeis stored zstd-compressed in a dedicated section of the shipped file, with a SHA-512 hash so a corrupted download is caught. On the first load, rspack reads that section, checks the hash, unzips it, and then rewrites the file compressed in place with the OS's transparent filesystem compression (APFS / NTFS / btrfs) so the kernel handles it from then on; on a filesystem that can't (ext4, most NFS) it falls back to unzipping once into a small per-version cache. Then it hands off to the normal addon. This is the same section layout thedecmpfscrate already reads, so that unwrap code is reused.What's in this PR
crates/node_binding/scripts/pack-compressed-binding.mjs— a reference packer that builds the compressed payload (zstd + SHA-512) in the exact layoutdecmpfsreads. Verified: it packs a real.nodeand unpacks to byte-identical bytes that load cleanly.crates/node_binding/scripts/pack-compressed-binding.test.mjs—node:testunit tests for the packer: pack/unpack round-trip, magic + header shape, and rejection of a short buffer, wrong magic, tampered payload (SHA-512 mismatch), and an implausible size. Run:node --test crates/node_binding/scripts/pack-compressed-binding.test.mjs.crates/node_binding/scripts/bench-compressed-binding.mjs— resolves whichever@rspack/bindingis installed for the current platform (no hardcoded version or path) and reports the size numbers above.crates/node_binding/docs/compressed-binding.md— a short design doc.What's still to do (on rspack's CI)
.nodefor each platform.Both need rspack's cross-platform build to test, which is why this is a draft.
We plan to upstream this to
@napi-rs/clieventually. Once the CLI can compress the addon itself, this shrinks to a one-line build flag and the extra code here is no longer needed. Until then it keeps the win in rspack's tree.