NURLNURL registrynurl-lang.org →

← all packages

wasmbuilder

owner @Hindurable

Install

[dependencies]
wasmbuilder = "^0.1.3"

Versions

Dependencies (latest)

None.

wasmbuilder

Compile NURL to wasm32-wasi locally — no wasi-sdk, no Docker, no build service. One install command brings everything:

curl -fsSL https://nurl-lang.org/install.sh | sh
export PATH="$HOME/.nurl/bin:$PATH"
nurlpkg install wasmbuilder

wasmbuilder program.nu            # → program.wasm

Run the result with any wasm runtime — the reference wasmtime, or the pure-NURL one from this registry:

nurlpkg install wasmtime
wt run program.wasm

How it works

The installed toolchain already contains everything a wasm build needs; wasmbuilder just drives it:

  1. nurlc compiles your program to LLVM IR (host-targeted).
  2. The IR rewriter retargets that IR for wasm32-unknown-wasi. This is

the production pipeline extracted from the play.nurl-lang.org build service: @main is renamed to the wasi crt entry, libc calls whose NURL FFI uses 64-bit widths where wasm32's libc expects 32 get trunc/zext shim wrappers, POSIX-only symbols wasi-libc doesn't ship (mmap, fork, pthread…) become error-sentinel stubs behind the stdlib's runtime gates, and every remaining external symbol is annotated as a potential env wasm import — so canvas/audio/GPU host imports resolve at run time instead of failing the link.

  1. The toolchain's bundled zig cc (which nurl.sh already prefers

for native builds) compiles + links the module: zig carries its own wasi-libc and wasm-ld, which is what makes wasi-sdk unnecessary.

  1. The NURL runtime (runtime.wasm.o) is compiled from the installed

stdlib's runtime.c on first use and cached under $NURL_HOME/build/wasmbuilder/, keyed by the source's content hash — upgrading the toolchain automatically rebuilds a matching runtime.

If no zig is found at all (for example a Windows install, or a Linux toolchain installed with the clang fallback), wasmbuilder downloads the pinned zig 0.13.0 release once, sha256-verified against ziglang.org's release index, into $NURL_HOME/zig — after which builds are fully offline. Set NURL_WASM_NO_DOWNLOAD=1 to forbid the download and fail instead.

CLI

wasmbuilder <file.nu> [options]

  -o, --output FILE      output path (default: <input>.wasm)
  -O, --opt LEVEL        0..3 or z (default 2; z = size)
      --emit-ll          keep the rewritten wasm32 .ll next to the output
      --asyncify         wasm-opt asyncify wrap for canvas programs
                         (needs binaryen's wasm-opt on PATH)
      --no-host-imports  don't pass --ffi-host-imports to nurlc
  -q, --quiet            suppress progress output
      --doctor           show how nurlc / zig / runtime resolve here
      --version, -h

--doctor is the first thing to run when something is off — it prints each resolution step (nurlc, stdlib C sources, wasm compiler, cache dir) and what would happen next.

Library use (embed the builder)

Other packages depend on wasmbuilder to compile wasm in-process — no subprocess orchestration, no HTTP build API. This is how swarm-mcp compiles compute kernels and how nurl-mcp exposes a local build-wasm tool:

$ `deps/wasmbuilder/src/build.nu`

: ~ WbOpts opts ( wb_opts_default )
= . opts quiet T
: !( Vec u ) String r ( wb_build_source kernel_src `kernel.nu` opts )
?? r {
    T wasm_bytes → { … ship to workers … }
    F err → { … structured error, nurlc diagnostics included … }
}

wb_build_file nu_path out_wasm opts → !v String is the file-to-file variant the CLI wraps; it also handles multi-file programs (imports resolve exactly as a native nurl build would).

Environment

VariableEffect
NURL_HOMEtoolchain prefix (default ~/.nurl)
NURLCexplicit nurlc binary
NURL_ZIGexplicit zig binary (skips discovery)
WASI_CLANGuse a wasi-sdk clang instead of zig
NURL_WASM_NO_DOWNLOADnever download zig; fail with instructions
NURL_WASM_OPTwasm-opt binary for --asyncify (default PATH)

Limitations

can't target wasm — the link reports the missing symbols.

run time (pthread stubs return error), mirroring the playground.

Tests

./tests/build_test.sh

Builds the CLI, compiles a corpus of repo examples to wasm, runs each next to its native twin and diffs the output byte-for-byte, then exercises the library API. The pipeline is also validated at scale: the NURL compiler itself (65k lines) built through wasmbuilder produces byte-identical output to its native twin under both the reference wasmtime and the pure-NURL wt.