feat(publish): unfurl import specifiers in Wasm modules (#34549)
## What
Publishing a module to JSR now unfurls the import specifiers found
inside a Wasm module, the same way it already does for JS/TS source
files.
A WebAssembly module's imports each reference a "module name" string
which Deno treats as an ES module specifier (e.g. `import { x } from
"./other.js"` compiled to Wasm imports from `"./other.js"`). Previously
these specifiers were passed through unchanged on publish, so an import
map / bare specifier inside a `.wasm` file would not resolve once
published. Now they are unfurled through the same `SpecifierUnfurler` /
workspace resolver used for textual modules.
Fixes #26955.
## How
- `cli/tools/publish/wasm.rs` (new): `unfurl_wasm()` walks the top-level
Wasm sections, and for the import section (id `2`) re-encodes it with
each module name rewritten via a callback. It uses `wasmparser` to parse
imports and `wasm-encoder` to re-encode the section. **Only the import
section is re-encoded — every other section is copied verbatim**, so the
rest of the binary is left byte-for-byte identical. If the bytes aren't
a core Wasm module (magic + version 1), or no specifier changes, the
original bytes are returned untouched.
- `cli/tools/publish/module_content.rs`: the `MediaType::Wasm` arm of
`resolve_content_maybe_unfurling` (which previously returned the bytes
as-is) now calls `unfurl_wasm`, resolving each import module name
through `SpecifierUnfurler::unfurl_specifier_reporting_diagnostic` with
`ResolutionKind::Execution`. Since Wasm is binary, diagnostics are
reported against the referrer with no source code frame.
- Adds `wasm-encoder` (with the `wasmparser` feature, for the reencode
helpers) and `wasmparser` as direct dependencies of the `deno` crate.
Both were already in the lockfile transitively at `0.244.0`.
## Tests
- Unit tests in `wasm.rs` cover specifier rewriting, the no-op cases
(nothing unfurled, non-Wasm bytes), validity of the re-encoded module,
and the LEB128 reader.
- `test_module_content_wasm` in `module_content.rs` exercises the full
path through the real `SpecifierUnfurler` + `WorkspaceResolver` (the
exact function the publish tar flow calls), asserting that bare/npm
specifiers are remapped via the import map while relative specifiers are
left as-is.
Closes denoland/divybot#264
Co-authored-by: divybot <divybot@users.noreply.github.com>
Co-authored-by: Divy Srivastava <me@littledivy.com> E
em committed
ec6dbb5f9d8c7fc30bc5c9a2c69af97a8f45e550
Parent: a65194c
Committed by GitHub <noreply@github.com>
on 6/1/2026, 7:21:01 AM