mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-13 21:11:22 +00:00
Copy doc comments from Rust to JS (#265)
* backend comments complete * better matching * gen comments * Add example * Move test bindings gen to own fn * move build step into build fn * add fn to read js, refactor gen_bindings/test to allow for this * Add comments test * Update readmes * add comments to travis * fix broken tests * +x on build.sh * fix wbg cmd in build.sh * Address fitzgen's comments
This commit is contained in:
committed by
Nick Fitzgerald
parent
3ad9bac599
commit
19d6cf1488
@ -61,6 +61,8 @@ matrix:
|
|||||||
(cd examples/char && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh)
|
(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/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:
|
install:
|
||||||
|
@ -52,6 +52,7 @@ members = [
|
|||||||
"examples/asm.js",
|
"examples/asm.js",
|
||||||
"examples/char",
|
"examples/char",
|
||||||
"examples/import_js",
|
"examples/import_js",
|
||||||
|
"examples/comments"
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -20,6 +20,7 @@ pub struct Export {
|
|||||||
pub mutable: bool,
|
pub mutable: bool,
|
||||||
pub constructor: Option<String>,
|
pub constructor: Option<String>,
|
||||||
pub function: Function,
|
pub function: Function,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||||
@ -82,6 +83,7 @@ pub struct Function {
|
|||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub fields: Vec<StructField>,
|
pub fields: Vec<StructField>,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||||
@ -92,12 +94,14 @@ pub struct StructField {
|
|||||||
pub ty: syn::Type,
|
pub ty: syn::Type,
|
||||||
pub getter: Ident,
|
pub getter: Ident,
|
||||||
pub setter: Ident,
|
pub setter: Ident,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||||
pub struct Enum {
|
pub struct Enum {
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub variants: Vec<Variant>,
|
pub variants: Vec<Variant>,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
#[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);
|
f.to_tokens(tokens);
|
||||||
self.exports.push(Export {
|
self.exports.push(Export {
|
||||||
class: None,
|
class: None,
|
||||||
@ -157,6 +162,7 @@ impl Program {
|
|||||||
mutable: false,
|
mutable: false,
|
||||||
constructor: None,
|
constructor: None,
|
||||||
function: Function::from(f, opts),
|
function: Function::from(f, opts),
|
||||||
|
comments,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
syn::Item::Struct(mut s) => {
|
syn::Item::Struct(mut s) => {
|
||||||
@ -237,6 +243,7 @@ impl Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let opts = BindgenAttrs::find(&mut method.attrs);
|
let opts = BindgenAttrs::find(&mut method.attrs);
|
||||||
|
let comments = extract_doc_comments(&method.attrs);
|
||||||
let is_constructor = opts.constructor();
|
let is_constructor = opts.constructor();
|
||||||
let constructor = if is_constructor {
|
let constructor = if is_constructor {
|
||||||
Some(method.sig.ident.to_string())
|
Some(method.sig.ident.to_string())
|
||||||
@ -259,6 +266,7 @@ impl Program {
|
|||||||
mutable: mutable.unwrap_or(false),
|
mutable: mutable.unwrap_or(false),
|
||||||
constructor,
|
constructor,
|
||||||
function,
|
function,
|
||||||
|
comments,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,9 +307,11 @@ impl Program {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
let comments = extract_doc_comments(&item.attrs);
|
||||||
self.enums.push(Enum {
|
self.enums.push(Enum {
|
||||||
name: item.ident,
|
name: item.ident,
|
||||||
variants,
|
variants,
|
||||||
|
comments,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,6 +595,7 @@ impl Export {
|
|||||||
method: self.method,
|
method: self.method,
|
||||||
constructor: self.constructor.clone(),
|
constructor: self.constructor.clone(),
|
||||||
function: self.function.shared(),
|
function: self.function.shared(),
|
||||||
|
comments: self.comments.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,6 +605,7 @@ impl Enum {
|
|||||||
shared::Enum {
|
shared::Enum {
|
||||||
name: self.name.to_string(),
|
name: self.name.to_string(),
|
||||||
variants: self.variants.iter().map(|v| v.shared()).collect(),
|
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 getter = shared::struct_field_get(&ident, &name_str);
|
||||||
let setter = shared::struct_field_set(&ident, &name_str);
|
let setter = shared::struct_field_set(&ident, &name_str);
|
||||||
let opts = BindgenAttrs::find(&mut field.attrs);
|
let opts = BindgenAttrs::find(&mut field.attrs);
|
||||||
|
let comments = extract_doc_comments(&field.attrs);
|
||||||
fields.push(StructField {
|
fields.push(StructField {
|
||||||
opts,
|
opts,
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
@ -757,12 +770,15 @@ impl Struct {
|
|||||||
ty: field.ty.clone(),
|
ty: field.ty.clone(),
|
||||||
getter: Ident::new(&getter, Span::call_site()),
|
getter: Ident::new(&getter, Span::call_site()),
|
||||||
setter: Ident::new(&setter, Span::call_site()),
|
setter: Ident::new(&setter, Span::call_site()),
|
||||||
|
comments
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let comments: Vec<String> = extract_doc_comments(&s.attrs);
|
||||||
Struct {
|
Struct {
|
||||||
name: s.ident.clone(),
|
name: s.ident.clone(),
|
||||||
fields,
|
fields,
|
||||||
|
comments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,6 +786,7 @@ impl Struct {
|
|||||||
shared::Struct {
|
shared::Struct {
|
||||||
name: self.name.to_string(),
|
name: self.name.to_string(),
|
||||||
fields: self.fields.iter().map(|s| s.shared()).collect(),
|
fields: self.fields.iter().map(|s| s.shared()).collect(),
|
||||||
|
comments: self.comments.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -779,6 +796,7 @@ impl StructField {
|
|||||||
shared::StructField {
|
shared::StructField {
|
||||||
name: self.name.to_string(),
|
name: self.name.to_string(),
|
||||||
readonly: self.opts.readonly(),
|
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);
|
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<String> {
|
||||||
|
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<String>
|
||||||
|
.fold(vec![], |mut acc, a| {acc.extend(a); acc})
|
||||||
|
}
|
@ -35,6 +35,7 @@ pub struct Context<'a> {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ExportedClass {
|
pub struct ExportedClass {
|
||||||
|
comments: String,
|
||||||
contents: String,
|
contents: String,
|
||||||
typescript: String,
|
typescript: String,
|
||||||
constructor: Option<String>,
|
constructor: Option<String>,
|
||||||
@ -42,6 +43,7 @@ pub struct ExportedClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ClassField {
|
struct ClassField {
|
||||||
|
comments: String,
|
||||||
name: String,
|
name: String,
|
||||||
readonly: bool,
|
readonly: bool,
|
||||||
}
|
}
|
||||||
@ -52,9 +54,12 @@ pub struct SubContext<'a, 'b: 'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
fn export(&mut self, name: &str, contents: &str) {
|
fn export(&mut self, name: &str, contents: &str, comments: Option<String>) {
|
||||||
let contents = deindent(contents);
|
let contents = deindent(contents);
|
||||||
let contents = contents.trim();
|
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 {
|
||||||
if contents.starts_with("class") {
|
if contents.starts_with("class") {
|
||||||
format!("{1}\nmodule.exports.{0} = {0};\n", name, contents)
|
format!("{1}\nmodule.exports.{0} = {0};\n", name, contents)
|
||||||
@ -396,7 +401,7 @@ impl<'a> Context<'a> {
|
|||||||
.with_context(|_| {
|
.with_context(|_| {
|
||||||
format!("failed to generate internal JS function `{}`", name)
|
format!("failed to generate internal JS function `{}`", name)
|
||||||
})?;
|
})?;
|
||||||
self.export(name, &contents);
|
self.export(name, &contents, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +466,7 @@ impl<'a> Context<'a> {
|
|||||||
function(ptr) {{
|
function(ptr) {{
|
||||||
return addHeapObject({}.__construct(ptr));
|
return addHeapObject({}.__construct(ptr));
|
||||||
}}
|
}}
|
||||||
", name));
|
", name), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
for field in class.fields.iter() {
|
for field in class.fields.iter() {
|
||||||
@ -484,7 +489,10 @@ impl<'a> Context<'a> {
|
|||||||
.method(true)
|
.method(true)
|
||||||
.ret(&Some(descriptor))?
|
.ret(&Some(descriptor))?
|
||||||
.finish("", &format!("wasm.{}", wasm_getter));
|
.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("get ");
|
||||||
dst.push_str(&field.name);
|
dst.push_str(&field.name);
|
||||||
dst.push_str(&get);
|
dst.push_str(&get);
|
||||||
@ -504,13 +512,12 @@ impl<'a> Context<'a> {
|
|||||||
}}
|
}}
|
||||||
", shared::free_function(&name)));
|
", shared::free_function(&name)));
|
||||||
ts_dst.push_str("free(): void;\n");
|
ts_dst.push_str("free(): void;\n");
|
||||||
|
|
||||||
dst.push_str(&class.contents);
|
dst.push_str(&class.contents);
|
||||||
ts_dst.push_str(&class.typescript);
|
ts_dst.push_str(&class.typescript);
|
||||||
dst.push_str("}\n");
|
dst.push_str("}\n");
|
||||||
ts_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);
|
self.typescript.push_str(&ts_dst);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -534,7 +541,7 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
fn rewrite_imports(&mut self, module_name: &str) {
|
fn rewrite_imports(&mut self, module_name: &str) {
|
||||||
for (name, contents) in self._rewrite_imports(module_name) {
|
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;
|
return;
|
||||||
throw new Error('stack is not currently empty');
|
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');
|
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
|
// Ensure a blank line between adjacent items, and ensure everything is
|
||||||
// terminated with a newline.
|
// 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("\n");
|
||||||
}
|
}
|
||||||
self.globals.push_str(s);
|
self.globals.push_str(s);
|
||||||
@ -1452,14 +1459,16 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
self.generate_enum(e);
|
self.generate_enum(e);
|
||||||
}
|
}
|
||||||
for s in self.program.structs.iter() {
|
for s in self.program.structs.iter() {
|
||||||
self.cx.exported_classes
|
let mut class = self.cx.exported_classes
|
||||||
.entry(s.name.clone())
|
.entry(s.name.clone())
|
||||||
.or_insert_with(Default::default)
|
.or_insert_with(Default::default);
|
||||||
.fields
|
class.comments = format_doc_comments(&s.comments);
|
||||||
.extend(s.fields.iter().map(|s| {
|
class.fields
|
||||||
|
.extend(s.fields.iter().map(|f| {
|
||||||
ClassField {
|
ClassField {
|
||||||
name: s.name.clone(),
|
name: f.name.clone(),
|
||||||
readonly: s.readonly,
|
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)
|
let (js, ts) = Js2Rust::new(&export.function.name, self.cx)
|
||||||
.process(descriptor.unwrap_function())?
|
.process(descriptor.unwrap_function())?
|
||||||
.finish("function", &format!("wasm.{}", export.function.name));
|
.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.globals.push_str("\n");
|
||||||
self.cx.typescript.push_str("export ");
|
self.cx.typescript.push_str("export ");
|
||||||
self.cx.typescript.push_str(&ts);
|
self.cx.typescript.push_str(&ts);
|
||||||
@ -1498,6 +1507,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
.finish("", &format!("wasm.{}", wasm_name));
|
.finish("", &format!("wasm.{}", wasm_name));
|
||||||
let class = self.cx.exported_classes.entry(class_name.to_string())
|
let class = self.cx.exported_classes.entry(class_name.to_string())
|
||||||
.or_insert(ExportedClass::default());
|
.or_insert(ExportedClass::default());
|
||||||
|
class.contents.push_str(&format_doc_comments(&export.comments));
|
||||||
if !export.method {
|
if !export.method {
|
||||||
class.contents.push_str("static ");
|
class.contents.push_str("static ");
|
||||||
class.typescript.push_str("static ");
|
class.typescript.push_str("static ");
|
||||||
@ -1514,7 +1524,6 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
1 => Some(constructors[0].clone()),
|
1 => Some(constructors[0].clone()),
|
||||||
x @ _ => bail!("there must be only one constructor, not {}", x),
|
x @ _ => bail!("there must be only one constructor, not {}", x),
|
||||||
};
|
};
|
||||||
|
|
||||||
class.contents.push_str(&export.function.name);
|
class.contents.push_str(&export.function.name);
|
||||||
class.contents.push_str(&js);
|
class.contents.push_str(&js);
|
||||||
class.contents.push_str("\n");
|
class.contents.push_str("\n");
|
||||||
@ -1593,7 +1602,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
function() {{
|
function() {{
|
||||||
return addHeapObject({});
|
return addHeapObject({});
|
||||||
}}
|
}}
|
||||||
", obj));
|
", obj), None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1688,7 +1697,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
.catch(import.catch)
|
.catch(import.catch)
|
||||||
.process(descriptor.unwrap_function())?
|
.process(descriptor.unwrap_function())?
|
||||||
.finish(&target);
|
.finish(&target);
|
||||||
self.cx.export(&import.shim, &js);
|
self.cx.export(&import.shim, &js, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1698,7 +1707,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
for variant in enum_.variants.iter() {
|
for variant in enum_.variants.iter() {
|
||||||
variants.push_str(&format!("{}:{},", variant.name, variant.value));
|
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));
|
self.cx.typescript.push_str(&format!("export enum {} {{", enum_.name));
|
||||||
|
|
||||||
variants.clear();
|
variants.clear();
|
||||||
@ -1764,3 +1773,9 @@ fn deindent(s: &str) -> String {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn format_doc_comments(comments: &Vec<String>) -> String {
|
||||||
|
let body: String = comments.iter().map(|c| format!("*{}\n", c.trim_matches('"'))).collect();
|
||||||
|
format!("/**\n{}*/\n", body)
|
||||||
|
}
|
@ -64,12 +64,14 @@ pub struct Export {
|
|||||||
pub method: bool,
|
pub method: bool,
|
||||||
pub constructor: Option<String>,
|
pub constructor: Option<String>,
|
||||||
pub function: Function,
|
pub function: Function,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Enum {
|
pub struct Enum {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub variants: Vec<EnumVariant>,
|
pub variants: Vec<EnumVariant>,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
@ -87,12 +89,14 @@ pub struct Function {
|
|||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub fields: Vec<StructField>,
|
pub fields: Vec<StructField>,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct StructField {
|
pub struct StructField {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub readonly: bool,
|
pub readonly: bool,
|
||||||
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_function(struct_name: &str) -> String {
|
pub fn new_function(struct_name: &str) -> String {
|
||||||
|
@ -35,5 +35,6 @@ The examples here are:
|
|||||||
the generated WebAssembly to normal JS
|
the generated WebAssembly to normal JS
|
||||||
* `char` - an example of passing the rust `char` type to and from the js `string` type
|
* `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
|
* `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
|
[binaryen]: https://github.com/WebAssembly/binaryen
|
||||||
|
14
examples/comments/Cargo.toml
Normal file
14
examples/comments/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "comments"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["robert masen <r@robertmasen.pizza>"]
|
||||||
|
|
||||||
|
[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']
|
16
examples/comments/README.md
Normal file
16
examples/comments/README.md
Normal file
@ -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`
|
20
examples/comments/build.sh
Executable file
20
examples/comments/build.sh
Executable file
@ -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
|
90
examples/comments/index.html
Normal file
90
examples/comments/index.html
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
||||||
|
</head>
|
||||||
|
<style>
|
||||||
|
body > * {
|
||||||
|
font-family: sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#comments {
|
||||||
|
width: 500px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
margin: auto;
|
||||||
|
background: darkgrey;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
#comments > .comment {
|
||||||
|
width: calc(100% - 14px);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comments > .comment.blue {
|
||||||
|
border: 2px solid lightcyan;
|
||||||
|
}
|
||||||
|
#comments > .comment.pink {
|
||||||
|
border: 2px solid lightpink;
|
||||||
|
}
|
||||||
|
#comments > .comment > .comment-top {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
#comments > .comment > .comment-top > .count {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
#comments > .comment > .comment-bottom {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
#comments > .comment > .comment-bottom > .comment-title {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
#comments > .comment > .comment-bottom > .comment-text {
|
||||||
|
background: white;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
}
|
||||||
|
#new-comment {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
width: 500px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
#new-comment > .input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="comments"></div>
|
||||||
|
<div id="input-container">
|
||||||
|
<form id="new-comment">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" id="name" required validation-message="name is required" />
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="name">Comment</label>
|
||||||
|
<textarea type="text" id="comment" required validation-message="comment is required" ></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="add-comment-button">Add</button>
|
||||||
|
</form>
|
||||||
|
<span id="error"></span>
|
||||||
|
</div>
|
||||||
|
<script src='./index.js'></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
140
examples/comments/index.js
Normal file
140
examples/comments/index.js
Normal file
@ -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 = '';
|
||||||
|
}
|
10
examples/comments/package.json
Normal file
10
examples/comments/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
60
examples/comments/src/lib.rs
Normal file
60
examples/comments/src/lib.rs
Normal file
@ -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,
|
||||||
|
}
|
11
examples/comments/webpack.config.js
Normal file
11
examples/comments/webpack.config.js
Normal file
@ -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',
|
||||||
|
};
|
56
tests/all/comments.rs
Normal file
56
tests/all/comments.rs
Normal file
@ -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<String> {
|
||||||
|
js.lines().filter_map(|l| {
|
||||||
|
let trimmed = l.trim();
|
||||||
|
if trimmed.starts_with("* ") {
|
||||||
|
Some(trimmed[2..].to_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
@ -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) {
|
fn build(&mut self) -> (PathBuf, PathBuf) {
|
||||||
self.ensure_test_entry();
|
self.ensure_test_entry();
|
||||||
@ -393,50 +407,19 @@ impl Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test(&mut self) {
|
fn test(&mut self) {
|
||||||
let (root, target_dir) = self.build();
|
let (root, target_dir) = self.cargo_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");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
self.gen_bindings(&root, &target_dir);
|
||||||
let mut wasm = Vec::new();
|
let mut wasm = Vec::new();
|
||||||
File::open(root.join("out_bg.wasm"))
|
File::open(root.join("out_bg.wasm")).unwrap()
|
||||||
.unwrap()
|
.read_to_end(&mut wasm).unwrap();
|
||||||
.read_to_end(&mut wasm)
|
|
||||||
.unwrap();
|
|
||||||
let obj = cli::wasm2es6js::Config::new()
|
let obj = cli::wasm2es6js::Config::new()
|
||||||
.base64(true)
|
.base64(true)
|
||||||
.generate(&wasm)
|
.generate(&wasm)
|
||||||
.expect("failed to convert wasm to js");
|
.expect("failed to convert wasm to js");
|
||||||
File::create(root.join("out_bg.d.ts"))
|
|
||||||
.unwrap()
|
File::create(root.join("out_bg.d.ts")).unwrap()
|
||||||
.write_all(obj.typescript().as_bytes())
|
.write_all(obj.typescript().as_bytes()).unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// move files from the root into each test, it looks like this may be
|
// move files from the root into each test, it looks like this may be
|
||||||
// needed for webpack to work well when invoked concurrently.
|
// needed for webpack to work well when invoked concurrently.
|
||||||
@ -471,6 +454,33 @@ impl Project {
|
|||||||
run(&mut cmd, "node");
|
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)]
|
#[cfg(unix)]
|
||||||
@ -527,3 +537,4 @@ mod slice;
|
|||||||
mod structural;
|
mod structural;
|
||||||
mod u64;
|
mod u64;
|
||||||
mod webidl;
|
mod webidl;
|
||||||
|
mod comments;
|
||||||
|
Reference in New Issue
Block a user