Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Both #326

Closed
wants to merge 4 commits into from
Closed

Both #326

Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ matrix:
(cd examples/closures && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh)
- |
(cd examples/comments && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh)
- |
(cd examples/node-and-browser && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh && npm start)

# Build the guide.
- rust: stable
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ members = [
"examples/asm.js",
"examples/char",
"examples/import_js",
"examples/comments"
"examples/comments",
"examples/node_and_browser"
]

[profile.release]
Expand Down
11 changes: 5 additions & 6 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ pub struct SubContext<'a, 'b: 'a> {

impl<'a> Context<'a> {
fn export(&mut self, name: &str, contents: &str, comments: Option<String>) {
let contents = contents;
let contents = contents.trim();
if let Some(ref c) = comments {
self.globals.push_str(c);
}
let global = if self.config.nodejs {
let global = if self.config.nodejs || self.config.both {
if contents.starts_with("class") {
format!("{1}\nmodule.exports.{0} = {0};\n", name, contents)
} else {
Expand Down Expand Up @@ -360,7 +359,7 @@ impl<'a> Context<'a> {
.unwrap_or("wasm_bindgen"),
)
} else {
let import_wasm = if self.config.nodejs {
let import_wasm = if self.config.nodejs || self.config.both {
self.footer.push_str(&format!("wasm = require('./{}_bg');",
module_name));
format!("var wasm;")
Expand Down Expand Up @@ -870,7 +869,7 @@ impl<'a> Context<'a> {
self.global(&format!("
const TextEncoder = require('util').TextEncoder;
"));
} else if !(self.config.browser || self.config.no_modules) {
} else if !(self.config.browser || self.config.no_modules) || self.config.both {
self.global(&format!("
const TextEncoder = typeof self === 'object' && self.TextEncoder
? self.TextEncoder
Expand All @@ -890,7 +889,7 @@ impl<'a> Context<'a> {
self.global(&format!("
const TextDecoder = require('util').TextDecoder;
"));
} else if !(self.config.browser || self.config.no_modules) {
} else if !(self.config.browser || self.config.no_modules) || self.config.both {
self.global(&format!("
const TextDecoder = typeof self === 'object' && self.TextDecoder
? self.TextDecoder
Expand Down Expand Up @@ -1743,7 +1742,7 @@ 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()) {
if self.cx.config.nodejs {
if self.cx.config.nodejs || self.cx.config.both {
self.cx.imports.push_str(&format!("\
const {} = require('{}').{};\n\
", name, module, name));
Expand Down
75 changes: 58 additions & 17 deletions crates/cli-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct Bindgen {
nodejs: bool,
browser: bool,
no_modules: bool,
both: bool,
no_modules_global: Option<String>,
debug: bool,
typescript: bool,
Expand All @@ -40,6 +41,7 @@ impl Bindgen {
browser: false,
no_modules: false,
no_modules_global: None,
both: false,
debug: false,
typescript: false,
demangle: true,
Expand All @@ -66,6 +68,11 @@ impl Bindgen {
self
}

pub fn both(&mut self, both: bool) -> &mut Bindgen {
self.both = both;
self
}

pub fn no_modules_global(&mut self, name: &str) -> &mut Bindgen {
self.no_modules_global = Some(name.to_string());
self
Expand Down Expand Up @@ -178,13 +185,7 @@ 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)
.and_then(|mut f| f.write_all(shim.as_bytes()))
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
}
self.write_wasm_import(&module, &wasm_path)?;

let wasm_bytes = parity_wasm::serialize(module)?;
File::create(&wasm_path)
Expand All @@ -193,7 +194,56 @@ impl Bindgen {
Ok(())
}

fn write_wasm_import(&self, m: &Module, wasm_path: &PathBuf) -> Result<(), Error> {
let shim = if self.nodejs {
self.generate_node_wasm_import(m, &wasm_path)
} else if self.both {
self.generate_both_wasm_import(m, &wasm_path)
} else {
return Ok(())
};
let js_path = wasm_path.with_extension("js");
File::create(&js_path)
.and_then(|mut f| f.write_all(shim.as_bytes()))
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
Ok(())
}

fn generate_node_wasm_import(&self, m: &Module, path: &Path) -> String {
let mut shim = self.generate_import_shim(m);

shim.push_str(&format!("
const join = require('path').join;
const bytes = require('fs').readFileSync(join(__dirname, '{}'));
const wasmModule = new WebAssembly.Module(bytes);
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
module.exports = wasmInstance.exports;
", path.file_name().unwrap().to_str().unwrap()
));

reset_indentation(&shim)
}

fn generate_both_wasm_import(&self, m: &Module, path: &Path) -> String {
reset_indentation(&format!("
const bytesScript = `const join = require('path').join;
require('fs').readFileSync(join(__dirname, '{path}'));`;
module.exports = resolve();
function resolve() {{
if (typeof self === 'object') {{
return require('./{path}');
}} else {{
{import}
const bytes = eval(bytesScript);
const wasmModule = new WebAssembly.Module(bytes);
return new WebAssembly.Instance(wasmModule, imports).exports;
}}
}}
",path = path.file_name().unwrap().to_str().unwrap(),
import = &self.generate_import_shim(m)))
}

fn generate_import_shim(&self, m: &Module) -> String {
let mut imports = BTreeSet::new();
if let Some(i) = m.import_section() {
for i in i.entries() {
Expand All @@ -206,16 +256,7 @@ impl Bindgen {
for module in imports {
shim.push_str(&format!("imports['{0}'] = require('{0}');\n", module));
}

shim.push_str(&format!("
const join = require('path').join;
const bytes = require('fs').readFileSync(join(__dirname, '{}'));
const wasmModule = new WebAssembly.Module(bytes);
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
module.exports = wasmInstance.exports;
", path.file_name().unwrap().to_str().unwrap()));

reset_indentation(&shim)
shim
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/cli/src/bin/wasm-bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Options:
--browser Generate output that only works in a browser
--no-modules Generate output that only works in a browser (without modules)
--no-modules-global VAR Name of the global variable to initialize
--both Generate output that will work with both node.js and webpack
--typescript Output a TypeScript definition file (on by default)
--no-typescript Don't emit a *.d.ts file
--debug Include otherwise-extraneous debug checks in output
Expand All @@ -40,6 +41,7 @@ struct Args {
flag_nodejs: bool,
flag_browser: bool,
flag_no_modules: bool,
flag_both: bool,
flag_typescript: bool,
flag_no_typescript: bool,
flag_out_dir: Option<PathBuf>,
Expand Down Expand Up @@ -83,6 +85,7 @@ fn rmain(args: &Args) -> Result<(), Error> {
.nodejs(args.flag_nodejs)
.browser(args.flag_browser)
.no_modules(args.flag_no_modules)
.both(args.flag_both)
.debug(args.flag_debug)
.demangle(!args.flag_no_demangle)
.typescript(typescript);
Expand Down
17 changes: 17 additions & 0 deletions examples/node_and_browser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "node-and-browser"
version = "0.1.0"
authors = ["Robert Masen <[email protected]>"]

[lib]
crate-type = ["cdylib"]

[dependencies]
# Here we're using a path dependency to use what's already in this repository,
# but you'd use the commented out version below if you're copying this into your
# project.
wasm-bindgen = { path = "../.." }
#wasm-bindgen = "0.2"
[dependencies.pulldown-cmark]
version = "0.1.2"
default-features = false
23 changes: 23 additions & 0 deletions examples/node_and_browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Node and Browser


This directory is an example of using the `#[wasm_bindgen]` macro to create an
entry point that's callable from both the browser and Node.js.

You can build the example locally with:

```
$ ./build.sh
```
This will build the project and spin up `webpack-dev-server` which makes it viewable at http://localhost:8080
(or a higher port if 8080 is already bound).

To see this project used in Node.js you can exit the instance of `webpack-dev-server` and run

```
$ npm start
```

This will read in the `wasm-bindgen` (README.md)[../../README.md] file and convert it to HTML
you can find the results in `./out.html` once it is finished.

19 changes: 19 additions & 0 deletions examples/node_and_browser/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/sh

set -ex

# Build the `hello_world.wasm` file using Cargo/rustc
cargo +nightly build --target wasm32-unknown-unknown

# Run the `wasm-bindgen` CLI tool to postprocess the wasm file emitted by the
# Rust compiler to emit the JS support glue that's necessary
#
# Here we're using the version of the CLI in this repository, but for external
# usage you'd use the commented out version below
cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml --bin wasm-bindgen -- ../../target/wasm32-unknown-unknown/debug/node_and_browser.wasm --out-dir . --both
# wasm-bindgen ../../target/wasm32-unknown-unknown/hello_world.wasm --out-dir .

# Finally, package everything up using Webpack and start a server so we can
# browse the result
npm install
npm run serve
54 changes: 54 additions & 0 deletions examples/node_and_browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0;
border: 0;
margin: 0;
}
* {
box-sizing: border-box;
}
#app-container {
width: calc(100% - 20px);
height: calc(100% - 20px);
padding: 10px;
background: slategray;
display: flex;
flex-flow: row;
justify-content: space-between;
align-items: flex-start;
align-content: flex-start;
}
#md-container,
#html-container {
width: calc(50% - 10px);
height: 100%;
padding: 0 5px;
}
#html-container {
background: white;
}
#md-input-box {
resize: none;
width: 100%;
height: 100%;
}
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.10.0/github-markdown.min.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="app-container">
<div id="md-container">
<textarea id="md-input-box"></textarea>
</div>
<div id="html-container" class="markdown-body">
<div id="rendered"></div>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
48 changes: 48 additions & 0 deletions examples/node_and_browser/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
let wasm;
import('./node_and_browser.js').then(w => {
console.log('wasm_cmark_parse resolved');
wasm = w;
ready();
});

function parseMD(md) {
console.log('parseMD');
return wasm.parse_markdown(md);
}

let debounce;
function ready() {
console.log('ready');
renderMD();
let input = getInput();
if (!input) throw new Error('Unable to find MD input');
input.addEventListener('keyup', () => {
if (!debounce) {
debounce = setTimeout(renderMD, 2000);
} else {
clearTimeout(debounce);
debounce = setTimeout(renderMD, 2000);
}
});
}
function renderMD() {
console.log('renderMD');
debounce = null;
let input = getInput();
if (!input) throw new Error('Unable to find MD input');
let md = input.value;
let html = parseMD(md);
let target = getHTML();
if (!target) throw new Error('Unable to find div to render HTML');
target.innerHTML = html;
}

function getInput() {
console.log('getInput');
return document.getElementById('md-input-box');
}

function getHTML() {
console.log('getHTML');
return document.getElementById('rendered');
}
Loading