From 9753f9150b84424fb76be3d26fa75efd1ff03a9a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 20 Jul 2018 12:01:28 -0500 Subject: [PATCH] Allow renaming exported functions into JS (#525) Support the `js_name` attribute on exports as well as imports to allow exporting types as camelCase instead of snake_case, for example. Closes #221 --- CHANGELOG.md | 13 +++++++ crates/backend/src/ast.rs | 1 + crates/backend/src/codegen.rs | 2 +- crates/macro/src/parser.rs | 13 ++++--- guide/src/design/export-customization.md | 32 ++++++++++++++++ tests/all/classes.rs | 48 ++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b88dfe..a2be8a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ -------------------------------------------------------------------------------- +## 0.2.13 + +Currently unreleased + +### Added + +* Support the `#[wasm_bindgen(js_name = foo)]` attribute on exported functions + and methods to allow renaming an export to JS. This allows JS to call it by + one name and Rust to call it by another, for example using `camelCase` in JS + and `snake_case` in Rust + +-------------------------------------------------------------------------------- + ## 0.2.12 Released 2018-07-19. diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index a51c667c..8d54083c 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -20,6 +20,7 @@ pub struct Export { pub constructor: Option, pub function: Function, pub comments: Vec, + pub rust_name: Ident, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index bf74b3ab..09ee3ee7 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -297,7 +297,7 @@ impl ToTokens for ast::Export { 0 }; - let name = &self.function.name; + let name = &self.rust_name; let receiver = match self.method_self { Some(ast::MethodSelf::ByValue) => { let class = self.class.as_ref().unwrap(); diff --git a/crates/macro/src/parser.rs b/crates/macro/src/parser.rs index 0045678f..6955174e 100644 --- a/crates/macro/src/parser.rs +++ b/crates/macro/src/parser.rs @@ -477,10 +477,10 @@ impl ConvertToAst for syn::ForeignItemStatic { } } -impl ConvertToAst<()> for syn::ItemFn { +impl ConvertToAst for syn::ItemFn { type Target = ast::Function; - fn convert(self, (): ()) -> Self::Target { + fn convert(self, attrs: BindgenAttrs) -> Self::Target { match self.vis { syn::Visibility::Public(_) => {} _ => panic!("can only bindgen public functions"), @@ -492,7 +492,8 @@ impl ConvertToAst<()> for syn::ItemFn { panic!("can only bindgen safe functions"); } - function_from_decl(&self.ident, self.decl, self.attrs, self.vis, false).0 + let name = attrs.js_name().unwrap_or(&self.ident); + function_from_decl(name, self.decl, self.attrs, self.vis, false).0 } } @@ -584,8 +585,9 @@ impl<'a> MacroParse<(Option, &'a mut TokenStream)> for syn::Item { class: None, method_self: None, constructor: None, - function: f.convert(()), comments, + rust_name: f.ident.clone(), + function: f.convert(opts.unwrap_or_default()), }); } syn::Item::Struct(mut s) => { @@ -677,7 +679,7 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { }; let (function, method_self) = function_from_decl( - &method.sig.ident, + opts.js_name().unwrap_or(&method.sig.ident), Box::new(method.sig.decl.clone()), method.attrs.clone(), method.vis.clone(), @@ -690,6 +692,7 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { constructor, function, comments, + rust_name: method.sig.ident.clone(), }); } } diff --git a/guide/src/design/export-customization.md b/guide/src/design/export-customization.md index 7a1e61e0..bba032aa 100644 --- a/guide/src/design/export-customization.md +++ b/guide/src/design/export-customization.md @@ -51,3 +51,35 @@ possibilities! const f = new Foo(); console.log(f.get_contents()); ``` + +* `js_name` - this can be used to export a different name in JS than what + something is named in Rust, for example: + + ```rust + #[wasm_bindgen] + pub struct Foo { + contents: u32, + } + + #[wasm_bindgen(js_name = makeFoo)] + pub fn make_foo() -> Foo { + Foo { contents: 6 } + } + + #[wasm_bindgen] + impl Foo { + #[wasm_bindgen(js_name = getContents)] + pub fn get_contents(&self) -> u32 { + self.contents + } + } + ``` + + Here this can be used in JS as: + + ```js + import { makeFoo } from './my_module'; + + const foo = makeFoo(); + console.log(foo.getContents()); + ``` diff --git a/tests/all/classes.rs b/tests/all/classes.rs index d037f9bc..f08cd706 100644 --- a/tests/all/classes.rs +++ b/tests/all/classes.rs @@ -709,3 +709,51 @@ fn double_consume() { "#) .test(); } + +#[test] +fn rename_function_for_js() { + project() + .file("src/lib.rs", r#" + #![feature(use_extern_macros, wasm_import_module)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub struct Foo { } + + #[wasm_bindgen] + impl Foo { + #[wasm_bindgen(constructor)] + pub fn new() -> Foo { + let f = Foo {}; + f.foo(); + return f + } + + #[wasm_bindgen(js_name = bar)] + pub fn foo(&self) { + } + + } + + #[wasm_bindgen(js_name = js_foo)] + pub fn foo() {} + + #[wasm_bindgen] + pub fn bar() { + foo(); + } + "#) + .file("test.js", r#" + import { Foo, js_foo, bar } from "./out"; + + export function test() { + Foo.new().bar(); + js_foo(); + bar(); + } + "#) + .test(); +}