Skip to content

Commit

Permalink
Import memory and enable module() for Node.js
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Dec 15, 2024
1 parent 3c07e86 commit b909d44
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 44 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
* Add test coverage support for Node.js.
[#4348](https://github.com/rustwasm/wasm-bindgen/pull/4348)

* Support importing memory and using `wasm_bindgen::module()` in Node.js.
[#4349](https://github.com/rustwasm/wasm-bindgen/pull/4349)

### Changed

* Optional parameters are now typed as `T | undefined | null` to reflect the actual JS behavior.
Expand Down Expand Up @@ -50,7 +53,7 @@ Released 2024-12-07

### Added

* Add support for multi-threading in Node.js.
* Add support for compiling with `atomics` for Node.js.
[#4318](https://github.com/rustwasm/wasm-bindgen/pull/4318)

* Add `WASM_BINDGEN_TEST_DRIVER_TIMEOUT` environment variable to control the timeout to start and connect to the test driver.
Expand Down
103 changes: 64 additions & 39 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,12 @@ impl<'a> Context<'a> {

fn generate_node_imports(&self) -> String {
let mut imports = BTreeSet::new();
for import in self.module.imports.iter() {
for import in self
.module
.imports
.iter()
.filter(|i| !(matches!(i.kind, walrus::ImportKind::Memory(_))))
{
imports.insert(&import.module);
}

Expand Down Expand Up @@ -296,9 +301,27 @@ impl<'a> Context<'a> {
reset_indentation(&shim)
}

fn generate_node_wasm_loading(&self, path: &Path) -> String {
fn generate_node_wasm_loading(&mut self, path: &Path) -> String {
let mut shim = String::new();

let module_name = "wbg";
if let Some(mem) = self.module.memories.iter().next() {
if let Some(id) = mem.import {
self.module.imports.get_mut(id).module = module_name.to_string();
shim.push_str(&format!(
"imports.{module_name} = {{ memory: new WebAssembly.Memory({{"
));
shim.push_str(&format!("initial:{}", mem.initial));
if let Some(max) = mem.maximum {
shim.push_str(&format!(",maximum:{}", max));
}
if mem.shared {
shim.push_str(",shared:true");
}
shim.push_str("}) };");
}
}

if self.config.mode.uses_es_modules() {
// On windows skip the leading `/` which comes out when we parse a
// url to use `C:\...` instead of `\C:\...`
Expand Down Expand Up @@ -460,8 +483,6 @@ impl<'a> Context<'a> {
// With normal CommonJS node we need to defer requiring the wasm
// until the end so most of our own exports are hooked up
OutputMode::Node { module: false } => {
self.nodejs_memory();

js.push_str(&self.generate_node_imports());

js.push_str("let wasm;\n");
Expand Down Expand Up @@ -505,8 +526,6 @@ impl<'a> Context<'a> {
// and let the bundler/runtime take care of it.
// With Node we manually read the Wasm file from the filesystem and instantiate it.
OutputMode::Bundler { .. } | OutputMode::Node { module: true } => {
self.nodejs_memory();

for (id, js) in iter_by_import(&self.wasm_import_definitions, self.module) {
let import = self.module.imports.get_mut(*id);
import.module = format!("./{}_bg.js", module_name);
Expand All @@ -524,26 +543,17 @@ impl<'a> Context<'a> {
}
}

self.imports_post.push_str(
"\
let wasm;
export function __wbg_set_wasm(val) {
wasm = val;
}
",
);

if matches!(self.config.mode, OutputMode::Node { module: true }) {
let start = start.get_or_insert_with(String::new);
start.push_str(&self.generate_node_imports());
start.push_str(&self.generate_node_wasm_loading(Path::new(&format!(
"./{}_bg.wasm",
module_name
))));
}

match self.config.mode {
OutputMode::Bundler { .. } => {
self.imports_post.push_str(
"\
let wasm;
export function __wbg_set_wasm(val) {
wasm = val;
}
",
);

start.get_or_insert_with(String::new).push_str(&format!(
"\
import {{ __wbg_set_wasm }} from \"./{module_name}_bg.js\";
Expand All @@ -552,8 +562,26 @@ __wbg_set_wasm(wasm);"
}

OutputMode::Node { module: true } => {
start.get_or_insert_with(String::new).push_str(&format!(
"imports[\"./{module_name}_bg.js\"].__wbg_set_wasm(wasm);"
self.imports_post.push_str(
"\
let wasm;
let wasmModule;
export function __wbg_set_wasm(exports, module) {
wasm = exports;
wasmModule = module;
}
",
);

let start = start.get_or_insert_with(String::new);
start.push_str(&self.generate_node_imports());
start.push_str(&self.generate_node_wasm_loading(Path::new(&format!(
"./{}_bg.wasm",
module_name
))));

start.push_str(&format!(
"imports[\"./{module_name}_bg.js\"].__wbg_set_wasm(wasm, wasmModule);"
));
}

Expand Down Expand Up @@ -1029,14 +1057,6 @@ __wbg_set_wasm(wasm);"
Ok((js, ts))
}

fn nodejs_memory(&mut self) {
if let Some(mem) = self.module.memories.iter_mut().next() {
if let Some(id) = mem.import.take() {
self.module.imports.delete(id);
}
}
}

fn write_classes(&mut self) -> Result<(), Error> {
for (class, exports) in self.exported_classes.take().unwrap() {
self.write_class(&class, &exports)?;
Expand Down Expand Up @@ -3833,13 +3853,18 @@ __wbg_set_wasm(wasm);"

Intrinsic::Module => {
assert_eq!(args.len(), 0);
if !self.config.mode.no_modules() && !self.config.mode.web() {
bail!(

match self.config.mode {
OutputMode::Web | OutputMode::NoModules { .. } => {
"__wbg_init.__wbindgen_wasm_module"
}
OutputMode::Node { .. } => "wasmModule",
_ => bail!(
"`wasm_bindgen::module` is currently only supported with \
`--target no-modules` and `--target web`"
);
`--target no-modules`, `--target web` and `--target nodejs`"
),
}
"__wbg_init.__wbindgen_wasm_module".to_string()
.to_string()
}

Intrinsic::Exports => {
Expand Down
4 changes: 0 additions & 4 deletions crates/cli-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,6 @@ impl OutputMode {
matches!(self, OutputMode::NoModules { .. })
}

fn web(&self) -> bool {
matches!(self, OutputMode::Web)
}

fn esm_integration(&self) -> bool {
matches!(
self,
Expand Down

0 comments on commit b909d44

Please sign in to comment.