From 90193eab51aee29a5583036bbc00e6c202febe0a Mon Sep 17 00:00:00 2001 From: Tim Ryan Date: Sat, 17 Nov 2018 23:04:19 -0500 Subject: [PATCH] Adds support for #[wasm_bindgen(typescript_custom_section)]. --- crates/backend/src/ast.rs | 2 + crates/backend/src/encode.rs | 1 + crates/cli-support/src/js/mod.rs | 4 ++ crates/macro-support/src/parser.rs | 53 +++++++++++++++++-- crates/shared/src/lib.rs | 1 + guide/src/SUMMARY.md | 1 + .../typescript_custom_section.md | 34 ++++++++++++ 7 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 guide/src/reference/attributes/on-rust-exports/typescript_custom_section.md diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 6dcf799ad18..51d968a480d 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -22,6 +22,8 @@ pub struct Program { /// objects" in the sense that they represent a JS object with a particular /// shape in JIT parlance. pub dictionaries: Vec, + /// custom typescript sections to be included in the definition file + pub typescript_custom_sections: Vec, } /// A rust to js interface. Allows interaction with rust objects/functions diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index b82ca2a77ee..525b704adc5 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -46,6 +46,7 @@ fn shared_program<'a>(prog: &'a ast::Program, intern: &'a Interner) imports: prog.imports.iter() .map(|a| shared_import(a, intern)) .collect::, _>>()?, + typescript_custom_sections: prog.typescript_custom_sections.iter().map(|x| -> &'a str { &x }).collect(), // version: shared::version(), // schema_version: shared::SCHEMA_VERSION.to_string(), }) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index f45487cb06d..3466024e7a7 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2143,6 +2143,10 @@ impl<'a, 'b> SubContext<'a, 'b> { for s in self.program.structs.iter() { 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(()) } diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 776a7d4aab5..75963987988 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -207,6 +207,13 @@ impl BindgenAttrs { _ => false, }) } + + fn typescript_custom_section(&self) -> bool { + self.attrs.iter().any(|a| match *a { + BindgenAttr::TypescriptCustomSection => true, + _ => false, + }) + } } impl Parse for BindgenAttrs { @@ -244,6 +251,7 @@ pub enum BindgenAttr { Extends(syn::Path), VendorPrefix(Ident), Variadic, + TypescriptCustomSection, } impl Parse for BindgenAttr { @@ -334,6 +342,9 @@ impl Parse for BindgenAttr { }; return Ok(BindgenAttr::JsName(val, span)); } + if attr == "typescript_custom_section" { + return Ok(BindgenAttr::TypescriptCustomSection); + } Err(original.error("unknown attribute")) } @@ -810,11 +821,20 @@ impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { e.to_tokens(tokens); e.macro_parse(program, ())?; } - _ => bail_span!( - self, - "#[wasm_bindgen] can only be applied to a function, \ - struct, enum, impl, or extern block" - ), + syn::Item::Const(mut c) => { + let opts = match opts { + Some(opts) => opts, + 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(()) @@ -992,6 +1012,29 @@ impl MacroParse<()> for syn::ItemEnum { } } +impl MacroParse 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 for syn::ItemForeignMod { fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> { let mut errors = Vec::new(); diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 8fc8ad7eba6..7dd023407d4 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -12,6 +12,7 @@ struct Program<'a> { enums: Vec>, imports: Vec>, structs: Vec>, + typescript_custom_sections: Vec<&'a str>, // version: &'a str, // schema_version: &'a str, } diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 952f5b7743f..26b36caf241 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -76,6 +76,7 @@ - [`constructor`](./reference/attributes/on-rust-exports/constructor.md) - [`js_name = Blah`](./reference/attributes/on-rust-exports/js_name.md) - [`readonly`](./reference/attributes/on-rust-exports/readonly.md) + - [`typescript_custom_section`](./reference/attributes/on-rust-exports/typescript_custom_section.md) -------------------------------------------------------------------------------- diff --git a/guide/src/reference/attributes/on-rust-exports/typescript_custom_section.md b/guide/src/reference/attributes/on-rust-exports/typescript_custom_section.md new file mode 100644 index 00000000000..77cd8546d7f --- /dev/null +++ b/guide/src/reference/attributes/on-rust-exports/typescript_custom_section.md @@ -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.