Generate node.js require directives for --nodejs

This commit is contained in:
Alex Crichton 2018-03-29 01:47:44 -07:00
parent 67737a2661
commit a0bfb8103b
6 changed files with 148 additions and 31 deletions

View File

@ -15,7 +15,7 @@ pub struct Project {
files: Vec<(String, String)>,
debug: bool,
js: bool,
detect_node: bool,
node: bool,
}
pub fn project() -> Project {
@ -29,7 +29,7 @@ pub fn project() -> Project {
Project {
debug: true,
js: false,
detect_node: false,
node: false,
files: vec![
("Cargo.toml".to_string(), format!(r#"
[package]
@ -134,8 +134,8 @@ impl Project {
self
}
pub fn detect_node(&mut self, detect_node: bool) -> &mut Project {
self.detect_node = detect_node;
pub fn node(&mut self, node: bool) -> &mut Project {
self.node = node;
self
}
@ -194,6 +194,7 @@ impl Project {
cli::Bindgen::new()
.input_path(&as_a_module)
.typescript(true)
.nodejs(self.node)
.debug(self.debug)
.generate(&root)
.expect("failed to run bindgen");
@ -216,21 +217,28 @@ impl Project {
let cwd = env::current_dir().unwrap();
symlink_dir(&cwd.join("node_modules"), &root.join("node_modules")).unwrap();
let mut cmd = if cfg!(windows) {
let mut c = Command::new("cmd");
c.arg("/c");
c.arg("yarn");
c
if self.node {
let mut cmd = Command::new("node");
cmd.arg(root.join("out.js"))
.current_dir(&root);
run(&mut cmd, "node");
} else {
Command::new("yarn")
};
cmd.arg("webpack").current_dir(&root);
run(&mut cmd, "node");
let mut cmd = if cfg!(windows) {
let mut c = Command::new("cmd");
c.arg("/c");
c.arg("yarn");
c
} else {
Command::new("yarn")
};
cmd.arg("webpack").current_dir(&root);
run(&mut cmd, "node");
let mut cmd = Command::new("node");
cmd.arg(root.join("bundle.js"))
.current_dir(&root);
run(&mut cmd, "node");
let mut cmd = Command::new("node");
cmd.arg(root.join("bundle.js"))
.current_dir(&root);
run(&mut cmd, "node");
}
}
}

View File

@ -11,6 +11,7 @@ use super::Bindgen;
pub struct Context<'a> {
pub globals: String,
pub imports: String,
pub footer: String,
pub typescript: String,
pub exposed_globals: HashSet<&'static str>,
pub required_internal_exports: HashSet<&'static str>,
@ -52,10 +53,14 @@ impl<'a> Context<'a> {
}
let contents = f(self);
let contents = contents.trim();
let global = if contents.starts_with("function") {
format!("export function {} {}\n", name, &contents[8..])
let global = if self.config.nodejs {
format!("module.exports.{} = {};\n", name, contents)
} else {
format!("export const {} = {};\n", name, contents)
if contents.starts_with("function") {
format!("export function {} {}\n", name, &contents[8..])
} else {
format!("export const {} = {};\n", name, contents)
}
};
self.globals.push_str(&global);
};
@ -212,16 +217,26 @@ impl<'a> Context<'a> {
self.rewrite_imports(module_name);
let import_wasm = if self.config.nodejs {
self.footer.push_str(&format!("wasm = require('./{}_bg');",
module_name));
format!("var wasm;")
} else {
format!("import * as wasm from './{}_bg';", module_name)
};
let js = format!("
/* tslint:disable */
import * as wasm from './{module_name}_bg'; // imports from wasm file
{import_wasm}
{imports}
{globals}
{footer}
",
module_name = module_name,
import_wasm = import_wasm,
globals = self.globals,
imports = self.imports,
footer = self.footer,
);
self.unexport_unused_internal_exports();
@ -1019,8 +1034,17 @@ impl<'a, 'b> SubContext<'a, 'b> {
&export.function.name,
false,
&export.function);
self.cx.globals.push_str("export ");
if self.cx.config.nodejs {
self.cx.globals.push_str("module.exports.");
self.cx.globals.push_str(&export.function.name);
self.cx.globals.push_str(" = ");
} else {
self.cx.globals.push_str("export ");
}
self.cx.globals.push_str(&js);
if self.cx.config.nodejs {
self.cx.globals.push_str(";");
}
self.cx.globals.push_str("\n");
self.cx.typescript.push_str("export ");
self.cx.typescript.push_str(&ts);
@ -1517,9 +1541,17 @@ impl<'a, 'b> SubContext<'a, 'b> {
dst.push_str(&extra);
dst.push_str(&format!("{}\n}}", invoc));
self.cx.globals.push_str("export ");
self.cx.globals.push_str(&dst);
self.cx.globals.push_str("\n");
if self.cx.config.nodejs {
self.cx.globals.push_str("module.exports.");
self.cx.globals.push_str(&import.shim);
self.cx.globals.push_str(" = ");
self.cx.globals.push_str(&dst);
self.cx.globals.push_str(";\n");
} else {
self.cx.globals.push_str("export ");
self.cx.globals.push_str(&dst);
self.cx.globals.push_str("\n");
}
}
pub fn generate_enum(&mut self, enum_: &shared::Enum) {
@ -1546,9 +1578,15 @@ impl<'a, 'b> SubContext<'a, 'b> {
let name = import.js_namespace.as_ref().map(|s| &**s).unwrap_or(item);
if self.cx.imported_names.insert(name.to_string()) {
self.cx.imports.push_str(&format!("
import {{ {} }} from '{}';
", name, module));
if self.cx.config.nodejs {
self.cx.imports.push_str(&format!("
const {} = require('{}').{};
", name, module, name));
} else {
self.cx.imports.push_str(&format!("
import {{ {} }} from '{}';
", name, module));
}
}
}
match import.js_namespace {

View File

@ -4,6 +4,7 @@ extern crate serde_json;
extern crate wasm_gc;
use std::char;
use std::collections::BTreeSet;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
@ -86,6 +87,7 @@ impl Bindgen {
let mut cx = js::Context {
globals: String::new(),
imports: String::new(),
footer: String::new(),
typescript: format!("/* tslint:disable */\n"),
exposed_globals: Default::default(),
required_internal_exports: Default::default(),
@ -118,6 +120,13 @@ impl Bindgen {
}
let wasm_path = out_dir.join(format!("{}_bg", stem)).with_extension("wasm");
if self.nodejs {
let js_path = wasm_path.with_extension("js");
let shim = self.generate_node_wasm_import(&module, &wasm_path);
File::create(&js_path)?.write_all(shim.as_bytes())?;
}
let wasm_bytes = parity_wasm::serialize(module).map_err(|e| {
Error(format!("{:?}", e))
})?;
@ -127,6 +136,30 @@ impl Bindgen {
File::create(&wasm_path)?.write_all(&bytes)?;
Ok(())
}
fn generate_node_wasm_import(&self, m: &Module, path: &Path) -> String {
let mut imports = BTreeSet::new();
if let Some(i) = m.import_section() {
for i in i.entries() {
imports.insert(i.module());
}
}
let mut shim = String::new();
shim.push_str("let imports = {};\n");
for module in imports {
shim.push_str(&format!("imports['{0}'] = require('{0}');\n", module));
}
shim.push_str(&format!("
const bytes = require('fs').readFileSync('{}');
const wasmModule = new WebAssembly.Module(bytes);
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
module.exports = wasmInstance.exports;
", path.file_name().unwrap().to_str().unwrap()));
shim
}
}
fn extract_programs(module: &mut Module) -> Vec<shared::Program> {

View File

@ -3,7 +3,6 @@ extern crate test_support;
#[test]
fn works() {
test_support::project()
.detect_node(true)
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section)]

40
tests/node.rs Normal file
View File

@ -0,0 +1,40 @@
extern crate test_support;
#[test]
fn works() {
test_support::project()
.node(true)
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "./test")]
extern {
fn hit();
}
#[wasm_bindgen]
pub fn run() {
hit();
}
"#)
.file("test.js", r#"
const assert = require('assert');
const run = require('./out');
var called = false;
module.exports.hit = function() {
called = true;
};
module.exports.test = function() {
run();
assert.strictEqual(called, true);
};
"#)
.test();
}

View File

@ -3,7 +3,6 @@ extern crate test_support;
#[test]
fn works() {
test_support::project()
.detect_node(true)
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]