diff --git a/.travis.yml b/.travis.yml index 31c58032..8a95d8dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,6 +61,8 @@ matrix: (cd examples/char && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh) - | (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) install: diff --git a/Cargo.toml b/Cargo.toml index 6d9abcc9..7cd2b5e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ members = [ "examples/asm.js", "examples/char", "examples/import_js", + "examples/comments" ] [profile.release] diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 4e761889..371c06d5 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -20,6 +20,7 @@ pub struct Export { pub mutable: bool, pub constructor: Option, pub function: Function, + pub comments: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -82,6 +83,7 @@ pub struct Function { pub struct Struct { pub name: Ident, pub fields: Vec, + pub comments: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -92,12 +94,14 @@ pub struct StructField { pub ty: syn::Type, pub getter: Ident, pub setter: Ident, + pub comments: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] pub struct Enum { pub name: Ident, pub variants: Vec, + pub comments: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -150,6 +154,7 @@ impl Program { } _ => {} } + let comments = extract_doc_comments(&f.attrs); f.to_tokens(tokens); self.exports.push(Export { class: None, @@ -157,6 +162,7 @@ impl Program { mutable: false, constructor: None, function: Function::from(f, opts), + comments, }); } syn::Item::Struct(mut s) => { @@ -237,6 +243,7 @@ impl Program { } let opts = BindgenAttrs::find(&mut method.attrs); + let comments = extract_doc_comments(&method.attrs); let is_constructor = opts.constructor(); let constructor = if is_constructor { Some(method.sig.ident.to_string()) @@ -259,6 +266,7 @@ impl Program { mutable: mutable.unwrap_or(false), constructor, function, + comments, }); } @@ -299,9 +307,11 @@ impl Program { } }) .collect(); + let comments = extract_doc_comments(&item.attrs); self.enums.push(Enum { name: item.ident, variants, + comments, }); } @@ -585,6 +595,7 @@ impl Export { method: self.method, constructor: self.constructor.clone(), function: self.function.shared(), + comments: self.comments.clone(), } } } @@ -594,6 +605,7 @@ impl Enum { shared::Enum { name: self.name.to_string(), variants: self.variants.iter().map(|v| v.shared()).collect(), + comments: self.comments.clone(), } } } @@ -750,6 +762,7 @@ impl Struct { let getter = shared::struct_field_get(&ident, &name_str); let setter = shared::struct_field_set(&ident, &name_str); let opts = BindgenAttrs::find(&mut field.attrs); + let comments = extract_doc_comments(&field.attrs); fields.push(StructField { opts, name: name.clone(), @@ -757,12 +770,15 @@ impl Struct { ty: field.ty.clone(), getter: Ident::new(&getter, Span::call_site()), setter: Ident::new(&setter, Span::call_site()), + comments }); } } + let comments: Vec = extract_doc_comments(&s.attrs); Struct { name: s.ident.clone(), fields, + comments, } } @@ -770,6 +786,7 @@ impl Struct { shared::Struct { name: self.name.to_string(), fields: self.fields.iter().map(|s| s.shared()).collect(), + comments: self.comments.clone(), } } } @@ -779,6 +796,7 @@ impl StructField { shared::StructField { name: self.name.to_string(), readonly: self.opts.readonly(), + comments: self.comments.clone(), } } } @@ -1072,3 +1090,30 @@ fn replace_self(name: &Ident, item: &mut syn::ImplItem) { syn::visit_mut::VisitMut::visit_impl_item_mut(&mut Walk(name), item); } + +/// Extract the documentation comments from a Vec of attributes +fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec { + attrs + .iter() + .filter_map(|a| { + // if the path segments include an ident of "doc" we know this + // this is a doc comment + if a.path.segments.iter().any(|s| s.ident.to_string() == "doc") { + Some( + // We want to filter out any Puncts so just grab the Literals + a.tts.clone().into_iter().filter_map(|t| match t { + TokenTree::Literal(lit) => { + // this will always return the quoted string, we deal with + // that in the cli when we read in the comments + Some(lit.to_string()) + }, + _ => None, + }) + ) + } else { + None + } + }) + //Fold up the [[String]] iter we created into Vec + .fold(vec![], |mut acc, a| {acc.extend(a); acc}) +} \ No newline at end of file diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 2e9a0a81..11216f33 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -35,6 +35,7 @@ pub struct Context<'a> { #[derive(Default)] pub struct ExportedClass { + comments: String, contents: String, typescript: String, constructor: Option, @@ -42,6 +43,7 @@ pub struct ExportedClass { } struct ClassField { + comments: String, name: String, readonly: bool, } @@ -52,9 +54,12 @@ pub struct SubContext<'a, 'b: 'a> { } impl<'a> Context<'a> { - fn export(&mut self, name: &str, contents: &str) { + fn export(&mut self, name: &str, contents: &str, comments: Option) { let contents = deindent(contents); let contents = contents.trim(); + if let Some(ref c) = comments { + self.globals.push_str(c); + } let global = if self.config.nodejs { if contents.starts_with("class") { format!("{1}\nmodule.exports.{0} = {0};\n", name, contents) @@ -396,7 +401,7 @@ impl<'a> Context<'a> { .with_context(|_| { format!("failed to generate internal JS function `{}`", name) })?; - self.export(name, &contents); + self.export(name, &contents, None); Ok(()) } @@ -461,7 +466,7 @@ impl<'a> Context<'a> { function(ptr) {{ return addHeapObject({}.__construct(ptr)); }} - ", name)); + ", name), None); } for field in class.fields.iter() { @@ -484,7 +489,10 @@ impl<'a> Context<'a> { .method(true) .ret(&Some(descriptor))? .finish("", &format!("wasm.{}", wasm_getter)); - + if !dst.ends_with("\n") { + dst.push_str("\n"); + } + dst.push_str(&field.comments); dst.push_str("get "); dst.push_str(&field.name); dst.push_str(&get); @@ -504,13 +512,12 @@ impl<'a> Context<'a> { }} ", shared::free_function(&name))); ts_dst.push_str("free(): void;\n"); - dst.push_str(&class.contents); ts_dst.push_str(&class.typescript); dst.push_str("}\n"); ts_dst.push_str("}\n"); - self.export(&name, &dst); + self.export(&name, &dst, Some(class.comments.clone())); self.typescript.push_str(&ts_dst); Ok(()) @@ -534,7 +541,7 @@ impl<'a> Context<'a> { fn rewrite_imports(&mut self, module_name: &str) { for (name, contents) in self._rewrite_imports(module_name) { - self.export(&name, &contents); + self.export(&name, &contents, None); } } @@ -691,7 +698,7 @@ impl<'a> Context<'a> { return; throw new Error('stack is not currently empty'); } - "); + ", None); } } @@ -715,7 +722,7 @@ impl<'a> Context<'a> { throw new Error('slab is not currently empty'); }} }} - ", initial_values.len())); + ", initial_values.len()), None); } } @@ -1406,7 +1413,7 @@ impl<'a> Context<'a> { // Ensure a blank line between adjacent items, and ensure everything is // terminated with a newline. - while !self.globals.ends_with("\n\n\n") { + while !self.globals.ends_with("\n\n\n") && !self.globals.ends_with("*/\n") { self.globals.push_str("\n"); } self.globals.push_str(s); @@ -1452,14 +1459,16 @@ impl<'a, 'b> SubContext<'a, 'b> { self.generate_enum(e); } for s in self.program.structs.iter() { - self.cx.exported_classes + let mut class = self.cx.exported_classes .entry(s.name.clone()) - .or_insert_with(Default::default) - .fields - .extend(s.fields.iter().map(|s| { + .or_insert_with(Default::default); + class.comments = format_doc_comments(&s.comments); + class.fields + .extend(s.fields.iter().map(|f| { ClassField { - name: s.name.clone(), - readonly: s.readonly, + name: f.name.clone(), + readonly: f.readonly, + comments: format_doc_comments(&f.comments), } })); } @@ -1477,7 +1486,7 @@ impl<'a, 'b> SubContext<'a, 'b> { let (js, ts) = Js2Rust::new(&export.function.name, self.cx) .process(descriptor.unwrap_function())? .finish("function", &format!("wasm.{}", export.function.name)); - self.cx.export(&export.function.name, &js); + self.cx.export(&export.function.name, &js, Some(format_doc_comments(&export.comments))); self.cx.globals.push_str("\n"); self.cx.typescript.push_str("export "); self.cx.typescript.push_str(&ts); @@ -1498,6 +1507,7 @@ impl<'a, 'b> SubContext<'a, 'b> { .finish("", &format!("wasm.{}", wasm_name)); let class = self.cx.exported_classes.entry(class_name.to_string()) .or_insert(ExportedClass::default()); + class.contents.push_str(&format_doc_comments(&export.comments)); if !export.method { class.contents.push_str("static "); class.typescript.push_str("static "); @@ -1514,7 +1524,6 @@ impl<'a, 'b> SubContext<'a, 'b> { 1 => Some(constructors[0].clone()), x @ _ => bail!("there must be only one constructor, not {}", x), }; - class.contents.push_str(&export.function.name); class.contents.push_str(&js); class.contents.push_str("\n"); @@ -1593,7 +1602,7 @@ impl<'a, 'b> SubContext<'a, 'b> { function() {{ return addHeapObject({}); }} - ", obj)); + ", obj), None); Ok(()) } @@ -1688,7 +1697,7 @@ impl<'a, 'b> SubContext<'a, 'b> { .catch(import.catch) .process(descriptor.unwrap_function())? .finish(&target); - self.cx.export(&import.shim, &js); + self.cx.export(&import.shim, &js, None); Ok(()) } @@ -1698,7 +1707,7 @@ impl<'a, 'b> SubContext<'a, 'b> { for variant in enum_.variants.iter() { variants.push_str(&format!("{}:{},", variant.name, variant.value)); } - self.cx.export(&enum_.name, &format!("Object.freeze({{ {} }})", variants)); + self.cx.export(&enum_.name, &format!("Object.freeze({{ {} }})", variants), Some(format_doc_comments(&enum_.comments))); self.cx.typescript.push_str(&format!("export enum {} {{", enum_.name)); variants.clear(); @@ -1764,3 +1773,9 @@ fn deindent(s: &str) -> String { } ret } + + +fn format_doc_comments(comments: &Vec) -> String { + let body: String = comments.iter().map(|c| format!("*{}\n", c.trim_matches('"'))).collect(); + format!("/**\n{}*/\n", body) +} \ No newline at end of file diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 81f10c46..55a6df15 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -64,12 +64,14 @@ pub struct Export { pub method: bool, pub constructor: Option, pub function: Function, + pub comments: Vec, } #[derive(Deserialize, Serialize)] pub struct Enum { pub name: String, pub variants: Vec, + pub comments: Vec, } #[derive(Deserialize, Serialize)] @@ -87,12 +89,14 @@ pub struct Function { pub struct Struct { pub name: String, pub fields: Vec, + pub comments: Vec, } #[derive(Deserialize, Serialize)] pub struct StructField { pub name: String, pub readonly: bool, + pub comments: Vec, } pub fn new_function(struct_name: &str) -> String { diff --git a/examples/README.md b/examples/README.md index 617f6f44..b58d29bc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -35,5 +35,6 @@ The examples here are: the generated WebAssembly to normal JS * `char` - an example of passing the rust `char` type to and from the js `string` type * `import_js` - an example of importing local JS functionality into a crate +* `comments` - an example of how Rust comments are copied into js bindings [binaryen]: https://github.com/WebAssembly/binaryen diff --git a/examples/comments/Cargo.toml b/examples/comments/Cargo.toml new file mode 100644 index 00000000..521547e4 --- /dev/null +++ b/examples/comments/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "comments" +version = "0.1.0" +authors = ["robert masen "] + +[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" + +[lib] +crate-type = ['cdylib'] \ No newline at end of file diff --git a/examples/comments/README.md b/examples/comments/README.md new file mode 100644 index 00000000..968b4f9c --- /dev/null +++ b/examples/comments/README.md @@ -0,0 +1,16 @@ +# Comments + +This directory is an example of how the `#[wasm_bindgen]` macro will +move your Rust doc comments to [JSDoc](http://usejsdoc.org/) comments + +You can build the example locally with: + +``` +$ ./build.sh +``` + +(or running the two commands on Windows manually) + +You should see the doc comments have been copied into the `comments.js` file. + +If you wanted to run the project itself, simply run `npm run serve` \ No newline at end of file diff --git a/examples/comments/build.sh b/examples/comments/build.sh new file mode 100755 index 00000000..e70c1389 --- /dev/null +++ b/examples/comments/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -ex +# Build the comments project +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/comments.wasm --out-dir . +# 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 \ No newline at end of file diff --git a/examples/comments/index.html b/examples/comments/index.html new file mode 100644 index 00000000..4e42b1fc --- /dev/null +++ b/examples/comments/index.html @@ -0,0 +1,90 @@ + + + + + + +
+
+
+
+ + +
+
+ + +
+ +
+ +
+ + + diff --git a/examples/comments/index.js b/examples/comments/index.js new file mode 100644 index 00000000..f0bb93f0 --- /dev/null +++ b/examples/comments/index.js @@ -0,0 +1,140 @@ +const mod = import('./comments'); +let wasm; +mod.then(m => { + wasm = m; + let button = document.getElementById('add-comment-button'); + if (!button) return console.error('Unable to find add button'); + button.addEventListener('click', newComment); + displayComments(); +}); + +/** + * Click event handler for add button + * @param {MouseEvent} ev + */ +function newComment(ev) { + clearError(); + let name = document.getElementById('name'); + if (!name) return console.error('Failed to find name input'); + if (name.value == '') return displayError('Name cannot be blank'); + let comment = document.getElementById('comment'); + if (!comment) return console.error('Failed to find comment input'); + if (comment.value == '') return displayError('comment cannot be blank'); + addComment(name.value, comment.value); + name.form.reset(); + displayComments(); +} + +/** + * Add a comment to the list + * @param {string} name The name of the person submitting + * @param {string} content The actual text of the comment + */ +function addComment(name, content) { + let existing = comments(); + let count = existing.length; + existing.push(new wasm.Comment(name, content, count)); + storeComments(); +} + +/** + * Convert the rust comments to JSON comments and store + * in local storage + */ +function storeComments() { + let json = comments().map(c => { + console.log('mapping comments for storage', c); + return { + name: c.name(), + comment: c.comment(), + count: c.count, + } + }); + localStorage.setItem('comments', JSON.stringify(json)); +} +/** + * Get the comments from local storage and convert them to + * rust comments + */ +function getComments() { + let json = localStorage.getItem('comments'); + if (!json) return []; + let raw = JSON.parse(json); + return raw.map(c => new wasm.Comment(c.name, c.comment, c.count)); +} + +/**A in memory cache of the localStorage comments for this site */ +let cachedComments = null; +/** + * Get a list of comments for this page + * @param {boolean} refresh force a refresh from localStorage + */ +function comments(refresh = false) { + if (refresh || !cachedComments) { + cachedComments = getComments(); + } + return cachedComments; +} + +/** + * Clear the comments section and re-render with the + * current comments list + */ +function displayComments() { + let node = document.getElementById('comments'); + if (!node) return console.error('Failed to get comments container'); + while (node.hasChildNodes()) { + node.removeChild(node.lastChild); + } + for (let comment of comments()) { + node.appendChild(renderComment(comment)); + } +} + +/** + * Generate the HTML needed to display a single comment + * @param {Comment} comment the comment to display + * @returns {HTMLDivElement} The div containing the comment html + */ +function renderComment(comment) { + let cls = `comment ${comment.color() == wasm.Color.Blue ? 'blue' : 'pink'}`; + let div = document.createElement('div'); + div.setAttribute('class', cls); + let top = document.createElement('div'); + top.setAttribute('class', 'comment-top'); + let ct = document.createElement('span'); + ct.setAttribute('class', 'count'); + ct.appendChild(document.createTextNode(`${comment.count}:`)); + let name = document.createElement('span'); + name.setAttribute('class', 'user-name'); + name.appendChild(document.createTextNode(`${comment.name()}`)); + top.appendChild(ct); + top.appendChild(name); + let bottom = document.createElement('div'); + bottom.setAttribute('class', 'comment-bottom'); + let title = document.createElement('span'); + title.setAttribute('class', 'comment-title'); + title.appendChild(document.createTextNode('comment')); + bottom.appendChild(title); + let text = document.createElement('span'); + text.setAttribute('class', 'comment-text'); + text.appendChild(document.createTextNode(comment.comment())) + bottom.appendChild(text); + div.appendChild(top); + div.appendChild(bottom) + return div; +} + +function displayError(message) { + let e = document.getElementById('error'); + if (!e) return console.error('Failed to find error container'); + if (e.innerHTML != '') return setTimeout(displayError, 1000, message); + e.innerHTML = message; + setTimeout(clearError, 3000); +} + +function clearError() { + let e = document.getElementById('error'); + if (!e) return console.error('Failed to find error container'); + e.innerHTML = ''; +} \ No newline at end of file diff --git a/examples/comments/package.json b/examples/comments/package.json new file mode 100644 index 00000000..c9666e67 --- /dev/null +++ b/examples/comments/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "serve": "webpack-dev-server" + }, + "devDependencies": { + "webpack": "^4.8.1", + "webpack-cli": "^2.0.10", + "webpack-dev-server": "^3.1.0" + } +} diff --git a/examples/comments/src/lib.rs b/examples/comments/src/lib.rs new file mode 100644 index 00000000..aaabd7e1 --- /dev/null +++ b/examples/comments/src/lib.rs @@ -0,0 +1,60 @@ +#![feature(proc_macro, wasm_custom_section, wasm_import_module)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +/// A user defined comment +pub struct Comment { + name: String, + comment: String, + /// The position this comment + /// should exist at + pub count: u32, + color: Color, +} + + +#[wasm_bindgen] +impl Comment { + #[wasm_bindgen(method)] + /// The name of the user who + /// posted this comment + pub fn name(&self) -> String { + self.name.clone() + } + #[wasm_bindgen(method)] + /// The content of this comment + pub fn comment(&self) -> String { + self.comment.clone() + } + + #[wasm_bindgen(constructor)] + pub fn new(name: String, comment: String, count: u32) -> Comment { + let color = if count % 2 == 0 { + Color::Blue + } else { + Color::Pink + }; + Comment { + name, + comment, + count, + color, + } + } + #[wasm_bindgen(method)] + /// What color should this comment be + pub fn color(&self) -> Color { + self.color.clone() + } +} + +/// The border of a comment +#[wasm_bindgen] +#[derive(Clone)] +pub enum Color { + Blue, + Pink, +} \ No newline at end of file diff --git a/examples/comments/webpack.config.js b/examples/comments/webpack.config.js new file mode 100644 index 00000000..644d9bc3 --- /dev/null +++ b/examples/comments/webpack.config.js @@ -0,0 +1,11 @@ +const path = require('path'); + +module.exports = { + entry: "./index.js", + output: { + path: path.resolve(__dirname, "dist"), + filename: "index.js", + }, + mode: "development", + devtool: 'source-map', +}; diff --git a/tests/all/comments.rs b/tests/all/comments.rs new file mode 100644 index 00000000..f9e1dc21 --- /dev/null +++ b/tests/all/comments.rs @@ -0,0 +1,56 @@ +use super::project; + +#[test] +fn works() { + let mut p = project(); + p.file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + /// This comment should exist + pub fn annotated() -> String { + String::new() + } + + #[wasm_bindgen] + /// This comment should exist + pub struct Annotated { + /// This comment should not exist + a: String, + /// This comment should exist + pub b: u32 + } + + #[wasm_bindgen] + impl Annotated { + #[wasm_bindgen(method)] + /// This comment should exist + pub fn get_a(&self) -> String { + self.a.clone() + } + } + "#); + + let (root, target) = p.cargo_build(); + p.gen_bindings(&root, &target); + let js = p.read_js(); + let comments = extract_doc_comments(&js); + assert!(comments.iter().all(|c| c == "This comment should exist")); +} +/// Pull out all lines in a js string that start with +/// '* ', all other lines will either be comment start, comment +/// end or actual js lines. +fn extract_doc_comments(js: &str) -> Vec { + js.lines().filter_map(|l| { + let trimmed = l.trim(); + if trimmed.starts_with("* ") { + Some(trimmed[2..].to_owned()) + } else { + None + } + }).collect() +} \ No newline at end of file diff --git a/tests/all/main.rs b/tests/all/main.rs index 3b0a1030..b909788a 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -330,6 +330,20 @@ impl Project { )); } } + /// build + cargo cmd execution + fn cargo_build(&mut self) -> (PathBuf, PathBuf) { + let (root, target_dir) = self.build(); + let mut cmd = Command::new("cargo"); + cmd.arg("build") + .arg("--target") + .arg("wasm32-unknown-unknown") + .current_dir(&root) + .env("CARGO_TARGET_DIR", &target_dir) + // Catch any warnings in generated code because we don't want any + .env("RUSTFLAGS", "-Dwarnings"); + run(&mut cmd, "cargo"); + (root, target_dir) + } fn build(&mut self) -> (PathBuf, PathBuf) { self.ensure_test_entry(); @@ -393,50 +407,19 @@ impl Project { } fn test(&mut self) { - let (root, target_dir) = self.build(); - - let mut cmd = Command::new("cargo"); - cmd.arg("build") - .arg("--target") - .arg("wasm32-unknown-unknown") - .current_dir(&root) - .env("CARGO_TARGET_DIR", &target_dir) - // Catch any warnings in generated code because we don't want any - .env("RUSTFLAGS", "-Dwarnings"); - run(&mut cmd, "cargo"); - - let idx = IDX.with(|x| *x); - let out = target_dir.join(&format!("wasm32-unknown-unknown/debug/test{}.wasm", idx)); - - let as_a_module = root.join("out.wasm"); - fs::copy(&out, &as_a_module).unwrap(); - - let res = cli::Bindgen::new() - .input_path(&as_a_module) - .typescript(true) - .nodejs(self.node) - .debug(self.debug) - .generate(&root); - if let Err(e) = res { - for e in e.causes() { - println!("- {}", e); - } - panic!("failed"); - } + let (root, target_dir) = self.cargo_build(); + self.gen_bindings(&root, &target_dir); let mut wasm = Vec::new(); - File::open(root.join("out_bg.wasm")) - .unwrap() - .read_to_end(&mut wasm) - .unwrap(); + File::open(root.join("out_bg.wasm")).unwrap() + .read_to_end(&mut wasm).unwrap(); let obj = cli::wasm2es6js::Config::new() .base64(true) .generate(&wasm) .expect("failed to convert wasm to js"); - File::create(root.join("out_bg.d.ts")) - .unwrap() - .write_all(obj.typescript().as_bytes()) - .unwrap(); + + File::create(root.join("out_bg.d.ts")).unwrap() + .write_all(obj.typescript().as_bytes()).unwrap(); // move files from the root into each test, it looks like this may be // needed for webpack to work well when invoked concurrently. @@ -471,6 +454,33 @@ impl Project { run(&mut cmd, "node"); } } + /// execute the cli against the current test .wasm + fn gen_bindings(&self, root: &PathBuf, target_dir: &PathBuf) { + let idx = IDX.with(|x| *x); + let out = target_dir.join(&format!("wasm32-unknown-unknown/debug/test{}.wasm", idx)); + + let as_a_module = root.join("out.wasm"); + fs::copy(&out, &as_a_module).unwrap(); + + let res = cli::Bindgen::new() + .input_path(&as_a_module) + .typescript(true) + .nodejs(self.node) + .debug(self.debug) + .generate(&root); + if let Err(e) = res { + for e in e.causes() { + println!("- {}", e); + } + panic!("failed"); + } + + } + fn read_js(&self) -> String { + let path = root().join("out.js"); + println!("js, {:?}", &path); + fs::read_to_string(path).expect("Unable to read js") + } } #[cfg(unix)] @@ -527,3 +537,4 @@ mod slice; mod structural; mod u64; mod webidl; +mod comments;