Skip to content

fix(cli): properly load WASM package in JS binding template#3038

Merged
Brooooooklyn merged 8 commits into
napi-rs:mainfrom
lsndr:fix/properly-load-wasm-package-2
Dec 30, 2025
Merged

fix(cli): properly load WASM package in JS binding template#3038
Brooooooklyn merged 8 commits into
napi-rs:mainfrom
lsndr:fix/properly-load-wasm-package-2

Conversation

@lsndr

@lsndr lsndr commented Nov 28, 2025

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Allow forcing the WASI runtime via an environment variable (including a strict "error" mode).
  • Changes

    • Preserve and chain loader errors when WASI is forced to improve diagnostics.
    • Improve fallback resolution to locate the WASM runtime file more reliably and surface clearer errors on failure.

✏️ Tip: You can customize this high-level summary in your review settings.


Note

Strengthens WASI loading and asset resolution across templates and examples.

  • In js-binding.ts and examples/napi/index.cjs, attempt WASI loading when NAPI_RS_FORCE_WASI is set (even if native binding exists) and preserve/chain wasiBindingError causes; add errors to loadErrors.
  • In load-wasi-template.ts and examples/napi/example.wasi.cjs, locate .wasm via require.resolve('<pkg>/<file>.wasm') instead of path.resolve, with clearer errors when the WASM package is missing.

Written by Cursor Bugbot for commit 18fe2c3. This will update automatically on new commits. Configure here.

@graphite-app

graphite-app Bot commented Nov 28, 2025

Copy link
Copy Markdown

How to use the Graphite Merge Queue

Add the label ready-to-merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@coderabbitai

coderabbitai Bot commented Nov 28, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

WASI fallback now triggers when NAPI_RS_FORCE_WASI is set or nativeBinding is absent; WASI load errors are chained/recorded defensively. WASM fallback resolves the package file directly via require.resolve('${packageName}-wasm32-wasi/${wasmFileName}.wasm') when the debug path is missing. Example usage updated to match direct wasm resolution and forced-WASI error handling.

Changes

Cohort / File(s) Summary
WASI binding loading
cli/src/api/templates/js-binding.ts
Fallback condition expanded to `if (!nativeBinding
WASM fallback resolution
cli/src/api/templates/load-wasi-template.ts
Fallback resolution now uses require.resolve('${packageName}-wasm32-wasi/${wasmFileName}.wasm') directly inside try/catch instead of resolving package root plus a generic path; control flow and error handling preserved.
Examples: WASI file resolution & forced-WASI handling
examples/napi/example.wasi.cjs, examples/napi/index.cjs
Example WASM path resolution tightened to require.resolve('@examples/napi-wasm32-wasi/example.wasm32-wasi.wasm'). index.cjs expands WASI fallback trigger to include NAPI_RS_FORCE_WASI, refines chaining of wasiBindingError, and adds explicit error path when NAPI_RS_FORCE_WASI === 'error' with missing WASI binding.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Loader as JS loader
  participant Native as nativeBinding
  participant WASI as wasiBinding
  participant FS as require.resolve (filesystem)

  Client->>Loader: require module
  Loader->>Native: attempt load nativeBinding
  alt nativeBinding loaded and not forcing WASI
    Native-->>Loader: success
    Loader-->>Client: export native API
  else nativeBinding missing or NAPI_RS_FORCE_WASI set
    Loader->>WASI: attempt load wasiBinding
    note right of WASI `#DDEBF7`: new path resolution may use FS for wasm file
    WASI->>FS: require.resolve('${packageName}-wasm32-wasi/${wasmFileName}.wasm')
    alt wasiBinding loaded
      WASI-->>Loader: success
      Loader-->>Client: export WASI API
    else wasiBinding load fails
      WASI-->>Loader: error
      alt NAPI_RS_FORCE_WASI == 'error'
        Loader-->>Client: throw forced-WASI not-found error
      else
        Loader-->>Client: push/record errors (chain cause if present)
      end
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hop through flags and files tonight,
I nudge a binding left or right,
I tuck each error as a gentle cause,
And hunt the wasm in package claws.
Small hops, neat links — a rabbit's byte.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request: fixing proper WASM package loading in the JS binding template through enhanced fallback logic and improved error handling.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 088534e and 18fe2c3.

📒 Files selected for processing (2)
  • examples/napi/example.wasi.cjs
  • examples/napi/index.cjs
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-11-25T09:31:23.877Z
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.877Z
Learning: Run `yarn workspace examples/napi build` before testing to ensure code generation reflects current Rust changes

Applied to files:

  • examples/napi/example.wasi.cjs
📚 Learning: 2025-11-25T09:31:23.877Z
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.877Z
Learning: Rebuild TypeScript definitions after making changes to Rust code using `yarn workspace examples/napi build`

Applied to files:

  • examples/napi/example.wasi.cjs
📚 Learning: 2025-11-25T09:31:23.877Z
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.877Z
Learning: Applies to /crates/**/*.rs : Use `#[napi]` attribute on impl blocks to define classes exported to JavaScript/TypeScript

Applied to files:

  • examples/napi/example.wasi.cjs
  • examples/napi/index.cjs
📚 Learning: 2025-11-25T09:31:23.877Z
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.877Z
Learning: Applies to /crates/**/*.rs : Use `#[napi(js_name = "...")]` attribute to rename Rust items in JavaScript/TypeScript

Applied to files:

  • examples/napi/example.wasi.cjs
📚 Learning: 2025-11-25T09:31:23.877Z
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.877Z
Learning: Run `cargo test` for Rust unit tests and `yarn workspace examples/napi test` for integration tests

Applied to files:

  • examples/napi/example.wasi.cjs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (42)
  • GitHub Check: Zig-Cross-Compile-On-Linux (aarch64-unknown-linux-musl)
  • GitHub Check: Zig-Cross-Compile-On-Linux (aarch64-apple-darwin)
  • GitHub Check: Zig-Cross-Compile-On-Linux (x86_64-unknown-linux-musl)
  • GitHub Check: Zig-Cross-Compile-On-Linux (armv7-unknown-linux-musleabihf)
  • GitHub Check: x86_64-unknown-linux-gnu - node@20 - toolchain@ stable
  • GitHub Check: aarch64-apple-darwin - node@24 - toolchain@ stable
  • GitHub Check: x86_64-unknown-linux-gnu - node@24 - toolchain@ 1.88.0
  • GitHub Check: Memory leak detect job
  • GitHub Check: aarch64-pc-windows-msvc - node@24 - toolchain@ stable
  • GitHub Check: aarch64-apple-darwin - node@20 - toolchain@ stable
  • GitHub Check: i686-pc-windows-msvc - node@24 - toolchain@ stable
  • GitHub Check: aarch64-pc-windows-msvc - node@22 - toolchain@ stable
  • GitHub Check: x86_64-pc-windows-msvc - node@20 - toolchain@ stable
  • GitHub Check: x86_64-pc-windows-msvc - node@22 - toolchain@ stable
  • GitHub Check: aarch64-apple-darwin - node@22 - toolchain@ stable
  • GitHub Check: x86_64-unknown-linux-gnu - node@22 - toolchain@ 1.88.0
  • GitHub Check: ASAN - ubuntu-24.04
  • GitHub Check: x86_64-pc-windows-msvc - node@24 - toolchain@ stable
  • GitHub Check: x86_64-unknown-linux-gnu - node@22 - toolchain@ stable
  • GitHub Check: i686-pc-windows-msvc - node@22 - toolchain@ stable
  • GitHub Check: x86_64-unknown-linux-gnu - node@24 - toolchain@ stable
  • GitHub Check: ASAN - windows-latest
  • GitHub Check: Test node wasi target
  • GitHub Check: stable - armv7-unknown-linux-gnueabihf - node@22
  • GitHub Check: build - powerpc64le-unknown-linux-gnu
  • GitHub Check: Build only test - loongarch64-unknown-linux-gnu
  • GitHub Check: Test node wasi target (--cfg tokio_unstable)
  • GitHub Check: build-and-test-msys2 (CLANG64)
  • GitHub Check: Build only test - aarch64-linux-android
  • GitHub Check: Build only test - armv7-linux-androideabi
  • GitHub Check: build-and-test-msys2 (UCRT64)
  • GitHub Check: Test latest bun
  • GitHub Check: Build only test - riscv64gc-unknown-linux-gnu
  • GitHub Check: build - aarch64-unknown-linux-gnu
  • GitHub Check: build-and-test-msys2 (MINGW64)
  • GitHub Check: Test freebsd target
  • GitHub Check: build - s390x-unknown-linux-gnu
  • GitHub Check: Build only test - aarch64-unknown-linux-ohos
  • GitHub Check: build - x86_64-unknown-linux-musl
  • GitHub Check: build - x86_64-unknown-linux-gnu
  • GitHub Check: build - aarch64-unknown-linux-musl
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
examples/napi/example.wasi.cjs (1)

40-46: LGTM! Direct .wasm file resolution is more reliable.

Using require.resolve() to directly resolve the .wasm file path rather than resolving the package root and appending the filename is a more robust approach. This properly leverages Node's module resolution algorithm to locate the exact file.

examples/napi/index.cjs (1)

539-558: LGTM! WASI fallback logic correctly expanded.

The changes correctly:

  1. Re-check the fallback condition (line 539) to ensure the package-based WASI binding is attempted when NAPI_RS_FORCE_WASI is set
  2. Chain errors defensively to preserve diagnostics (lines 545-549)
  3. Add an explicit error path for NAPI_RS_FORCE_WASI === 'error' mode (lines 554-558)

One minor observation: setting wasiBindingError.cause = err (line 548) chains the second error as the cause of the first, which is semantically inverted (the first failure wasn't caused by the second). However, for diagnostic purposes, all errors are preserved, so this is acceptable.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread cli/src/api/templates/js-binding.ts
Comment thread cli/src/api/templates/load-wasi-template.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cli/src/api/templates/js-binding.ts (1)

230-257: NAPI_RS_FORCE_WASI path can throw via null wasiBindingError when ${pkgName}-wasm32-wasi is missing

With the updated condition at Line 241:

if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {

the second WASI fallback is executed even when nativeBinding is already successfully set to a WASI binding from ./${localName}.wasi.cjs.

In the scenario:

  • process.env.NAPI_RS_FORCE_WASI is set (e.g. '1' or 'error'),
  • require('./${localName}.wasi.cjs') succeedsnativeBinding becomes truthy and wasiBindingError stays null,
  • require('${pkgName}-wasm32-wasi') then fails,

the catch at Lines 245–249 runs and does:

wasiBindingError.cause = err

but wasiBindingError is still null, so this will raise a TypeError while evaluating the binding, instead of giving a clean, intentional error.

You can avoid this by:

  1. Only trying the ${pkgName}-wasm32-wasi package when we still don’t have a WASI binding, and
  2. Guarding wasiBindingError before assigning cause.

A minimal patch:

-  if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
+  if (!nativeBinding) {
     try {
       wasiBinding = require('${pkgName}-wasm32-wasi')
       nativeBinding = wasiBinding
     } catch (err) {
       if (process.env.NAPI_RS_FORCE_WASI) {
-        wasiBindingError.cause = err
-        loadErrors.push(err)
+        if (wasiBindingError) {
+          wasiBindingError.cause = err
+        } else {
+          wasiBindingError = err
+        }
+        loadErrors.push(err)
       }
     }
   }

This still honors NAPI_RS_FORCE_WASI semantics:

  • Native binding is bypassed whenever the env var is set (via the outer if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI)),
  • You try ./${localName}.wasi.cjs first, then ${pkgName}-wasm32-wasi only if no WASI binding is loaded yet,
  • Aggregated error information for the 'error' mode remains intact, without risking a null dereference.
🧹 Nitpick comments (1)
cli/src/api/templates/load-wasi-template.ts (1)

141-148: Fallback to node_modules/${packageName}-wasm32-wasi is straightforward but assumes cwd layout

Pointing __wasmFilePath straight at:

__nodePath.resolve('node_modules/${packageName}-wasm32-wasi/${wasmFileName}.debug.wasm')

is a nice simplification and will work for the common case where the process runs from the project root.

If consumers might execute from subdirectories or in monorepos/hoisted setups, you may eventually want a more robust resolution (e.g. via require.resolve on the WASI package and then joining ${wasmFileName}.debug.wasm) so you follow Node’s module resolution instead of depending on process.cwd() and a flat node_modules layout. Not a blocker, just something to keep in mind.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4f5360 and 4661479.

📒 Files selected for processing (2)
  • cli/src/api/templates/js-binding.ts (1 hunks)
  • cli/src/api/templates/load-wasi-template.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.866Z
Learning: Applies to /crates/**/*.rs : Use `#[napi]` attribute on impl blocks to define classes exported to JavaScript/TypeScript
Learnt from: CR
Repo: napi-rs/napi-rs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:31:23.866Z
Learning: Applies to /crates/**/*.rs : Use `#[napi(js_name = "...")]` attribute to rename Rust items in JavaScript/TypeScript

@lsndr
lsndr marked this pull request as draft November 28, 2025 08:34
@lsndr
lsndr marked this pull request as ready for review November 28, 2025 09:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes WASM package loading in the JS binding template by improving support for the NAPI_RS_FORCE_WASI environment variable and updating the fallback path resolution for WASM files.

Key Changes:

  • Added explicit support for forcing WASI binding via the NAPI_RS_FORCE_WASI environment variable
  • Improved error handling to properly chain load errors when multiple attempts fail
  • Updated WASM file resolution to use a direct package path instead of generic require resolution

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
cli/src/api/templates/load-wasi-template.ts Changed WASM fallback path from try-catch with package resolution to direct node_modules path
cli/src/api/templates/js-binding.ts Enhanced NAPI_RS_FORCE_WASI support by checking it before package loading and fixing null-safe error chaining

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cli/src/api/templates/load-wasi-template.ts Outdated
Comment thread cli/src/api/templates/load-wasi-template.ts Outdated
Comment thread cli/src/api/templates/load-wasi-template.ts Outdated
@lsndr
lsndr force-pushed the fix/properly-load-wasm-package-2 branch from 551ad82 to 7c8ac81 Compare December 9, 2025 12:24
@lsndr

lsndr commented Dec 9, 2025

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Dec 9, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Brooooooklyn
Brooooooklyn force-pushed the fix/properly-load-wasm-package-2 branch from 0683bbc to 088534e Compare December 30, 2025 07:01
@Brooooooklyn
Brooooooklyn merged commit 60452d0 into napi-rs:main Dec 30, 2025
75 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants