Adds support for #[wasm_bindgen(typescript_custom_section)].

This commit is contained in:
Tim Ryan 2018-11-17 23:04:19 -05:00
parent fc0d6528fc
commit 90193eab51
7 changed files with 91 additions and 5 deletions

View File

@ -22,6 +22,8 @@ pub struct Program {
/// objects" in the sense that they represent a JS object with a particular /// objects" in the sense that they represent a JS object with a particular
/// shape in JIT parlance. /// shape in JIT parlance.
pub dictionaries: Vec<Dictionary>, pub dictionaries: Vec<Dictionary>,
/// custom typescript sections to be included in the definition file
pub typescript_custom_sections: Vec<String>,
} }
/// A rust to js interface. Allows interaction with rust objects/functions /// A rust to js interface. Allows interaction with rust objects/functions

View File

@ -46,6 +46,7 @@ fn shared_program<'a>(prog: &'a ast::Program, intern: &'a Interner)
imports: prog.imports.iter() imports: prog.imports.iter()
.map(|a| shared_import(a, intern)) .map(|a| shared_import(a, intern))
.collect::<Result<Vec<_>, _>>()?, .collect::<Result<Vec<_>, _>>()?,
typescript_custom_sections: prog.typescript_custom_sections.iter().map(|x| -> &'a str { &x }).collect(),
// version: shared::version(), // version: shared::version(),
// schema_version: shared::SCHEMA_VERSION.to_string(), // schema_version: shared::SCHEMA_VERSION.to_string(),
}) })

View File

@ -2143,6 +2143,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
for s in self.program.structs.iter() { for s in self.program.structs.iter() {
self.generate_struct(s)?; self.generate_struct(s)?;
} }
for s in self.program.typescript_custom_sections.iter() {
self.cx.typescript.push_str(s);
self.cx.typescript.push_str("\n\n");
}
Ok(()) Ok(())
} }

View File

@ -207,6 +207,13 @@ impl BindgenAttrs {
_ => false, _ => false,
}) })
} }
fn typescript_custom_section(&self) -> bool {
self.attrs.iter().any(|a| match *a {
BindgenAttr::TypescriptCustomSection => true,
_ => false,
})
}
} }
impl Parse for BindgenAttrs { impl Parse for BindgenAttrs {
@ -244,6 +251,7 @@ pub enum BindgenAttr {
Extends(syn::Path), Extends(syn::Path),
VendorPrefix(Ident), VendorPrefix(Ident),
Variadic, Variadic,
TypescriptCustomSection,
} }
impl Parse for BindgenAttr { impl Parse for BindgenAttr {
@ -334,6 +342,9 @@ impl Parse for BindgenAttr {
}; };
return Ok(BindgenAttr::JsName(val, span)); return Ok(BindgenAttr::JsName(val, span));
} }
if attr == "typescript_custom_section" {
return Ok(BindgenAttr::TypescriptCustomSection);
}
Err(original.error("unknown attribute")) Err(original.error("unknown attribute"))
} }
@ -810,11 +821,20 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
e.to_tokens(tokens); e.to_tokens(tokens);
e.macro_parse(program, ())?; e.macro_parse(program, ())?;
} }
_ => bail_span!( syn::Item::Const(mut c) => {
self, let opts = match opts {
"#[wasm_bindgen] can only be applied to a function, \ Some(opts) => opts,
struct, enum, impl, or extern block" None => BindgenAttrs::find(&mut c.attrs)?,
), };
c.macro_parse(program, opts)?;
}
_ => {
bail_span!(
self,
"#[wasm_bindgen] can only be applied to a function, \
struct, enum, impl, or extern block",
);
}
} }
Ok(()) Ok(())
@ -992,6 +1012,29 @@ impl MacroParse<()> for syn::ItemEnum {
} }
} }
impl MacroParse<BindgenAttrs> for syn::ItemConst {
fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
// Shortcut
if !opts.typescript_custom_section() {
bail_span!(self, "#[wasm_bindgen] will not work on constants unless you are defining a #[wasm_bindgen(typescript_custom_section)].");
}
match *self.expr {
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(litstr),
..
}) => {
program.typescript_custom_sections.push(litstr.value());
},
_ => {
bail_span!(self, "Expected a string literal to be used with #[wasm_bindgen(typescript_custom_section)].");
},
}
Ok(())
}
}
impl MacroParse<BindgenAttrs> for syn::ItemForeignMod { impl MacroParse<BindgenAttrs> for syn::ItemForeignMod {
fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> { fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
let mut errors = Vec::new(); let mut errors = Vec::new();

View File

@ -12,6 +12,7 @@ struct Program<'a> {
enums: Vec<Enum<'a>>, enums: Vec<Enum<'a>>,
imports: Vec<Import<'a>>, imports: Vec<Import<'a>>,
structs: Vec<Struct<'a>>, structs: Vec<Struct<'a>>,
typescript_custom_sections: Vec<&'a str>,
// version: &'a str, // version: &'a str,
// schema_version: &'a str, // schema_version: &'a str,
} }

View File

@ -76,6 +76,7 @@
- [`constructor`](./reference/attributes/on-rust-exports/constructor.md) - [`constructor`](./reference/attributes/on-rust-exports/constructor.md)
- [`js_name = Blah`](./reference/attributes/on-rust-exports/js_name.md) - [`js_name = Blah`](./reference/attributes/on-rust-exports/js_name.md)
- [`readonly`](./reference/attributes/on-rust-exports/readonly.md) - [`readonly`](./reference/attributes/on-rust-exports/readonly.md)
- [`typescript_custom_section`](./reference/attributes/on-rust-exports/typescript_custom_section.md)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -0,0 +1,34 @@
# `typescript_custom_section`
When added to a `const` `&'static str`, it will append the contents of the
string to the `.d.ts` file exported by `wasm-bindgen-cli` (when the
`--typescript` flag is enabled).
```rust
#[wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type Coords = { "latitude": number, "longitude": number, };
"#;
```
The primary target for this feature is for code generation. For example, you
can author a macro that allows you to export a TypeScript definition alongside
the definition of a struct or Rust type.
```rust
#[derive(MyTypescriptExport)]
struct Coords {
latitude: u32,
longitude: u32,
}
```
The proc_derive_macro "MyTypescriptExport" can export its own
`#[wasm_bindgen(typescript_custom_section)]` section, which would then be
picked up by wasm-bindgen-cli. This would be equivalent to the contents of
the TS_APPEND_CONTENT string in the first example.
This feature allows plain data objects to be typechecked in Rust and in
TypeScript by outputing a type definition generated at compile time.