diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 0991dac66cf..16de8e360b0 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -349,6 +349,9 @@ pub struct StringEnum { pub rust_attrs: Vec, /// Whether to generate a typescript definition for this enum pub generate_typescript: bool, + /// Whether the type generated for this string enum should be publicly + /// exported as part of the API of the generated JS module + pub export_type: bool, /// Path to wasm_bindgen pub wasm_bindgen: Path, } diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index baae8d72533..2e01237328a 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -364,6 +364,7 @@ fn shared_import_enum<'a>(i: &'a ast::StringEnum, _intern: &'a Interner) -> Stri StringEnum { name: &i.js_name, generate_typescript: i.generate_typescript, + export_type: i.export_type, variant_values: i.variant_values.iter().map(|x| &**x).collect(), comments: i.comments.iter().map(|s| &**s).collect(), } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index aff91c6cfcc..13a91a6e6a1 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3972,9 +3972,10 @@ __wbg_set_wasm(wasm);" .collect(); if string_enum.generate_typescript - && self - .typescript_refs - .contains(&TsReference::StringEnum(string_enum.name.clone())) + && (string_enum.export_type + || self + .typescript_refs + .contains(&TsReference::StringEnum(string_enum.name.clone()))) { let docs = format_doc_comments(&string_enum.comments, None); let type_expr = if variants.is_empty() { @@ -3984,6 +3985,9 @@ __wbg_set_wasm(wasm);" }; self.typescript.push_str(&docs); + if string_enum.export_type { + self.typescript.push_str("export "); + } self.typescript.push_str("type "); self.typescript.push_str(&string_enum.name); self.typescript.push_str(" = "); diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 07a09860e0c..78d5cc50e56 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -877,6 +877,7 @@ impl<'a> Context<'a> { .map(|v| v.to_string()) .collect(), generate_typescript: string_enum.generate_typescript, + export_type: string_enum.export_type, }; let mut result = Ok(()); self.aux diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs index e43a49988df..27f7822c394 100644 --- a/crates/cli-support/src/wit/nonstandard.rs +++ b/crates/cli-support/src/wit/nonstandard.rs @@ -185,6 +185,8 @@ pub struct AuxStringEnum { pub variant_values: Vec, /// Whether typescript bindings should be generated for this enum. pub generate_typescript: bool, + /// Whether typescript bindings should be exported. + pub export_type: bool, } #[derive(Debug)] diff --git a/crates/cli/tests/reference/enums.d.ts b/crates/cli/tests/reference/enums.d.ts index da84bbb7740..179b7a69cdd 100644 --- a/crates/cli/tests/reference/enums.d.ts +++ b/crates/cli/tests/reference/enums.d.ts @@ -40,3 +40,7 @@ export enum Ordering { * The name of a color. */ type ColorName = "green" | "yellow" | "red"; +/** + * An unused string enum that has its typed exported. + */ +export type FooBarBaz = "foo" | "bar"; diff --git a/crates/cli/tests/reference/enums.rs b/crates/cli/tests/reference/enums.rs index 553d63d9a4d..2ca4449383b 100644 --- a/crates/cli/tests/reference/enums.rs +++ b/crates/cli/tests/reference/enums.rs @@ -52,6 +52,13 @@ pub enum UnusedStringEnum { Bar = "bar", } +/// An unused string enum that has its typed exported. +#[wasm_bindgen(js_name = "FooBarBaz", export_type)] +pub enum ExportedUnusedStringEnum { + Foo = "foo", + Bar = "bar", +} + #[wasm_bindgen] enum PrivateStringEnum { Foo = "foo", diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 72bd849d859..b2b6b865bb6 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -95,6 +95,7 @@ macro_rules! attrgen { (typescript_type, TypeScriptType(Span, String, Span)), (getter_with_clone, GetterWithClone(Span)), (static_string, StaticString(Span)), + (export_type, ExportType(Span)), (thread_local, ThreadLocal(Span)), // For testing purposes only. @@ -1341,6 +1342,7 @@ fn string_enum( program: &mut ast::Program, js_name: String, generate_typescript: bool, + export_type: bool, comments: Vec, ) -> Result<(), Diagnostic> { let mut variants = vec![]; @@ -1380,6 +1382,7 @@ fn string_enum( comments, rust_attrs: enum_.attrs, generate_typescript, + export_type, wasm_bindgen: program.wasm_bindgen.clone(), }), }); @@ -1453,8 +1456,6 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum { .map_or_else(|| self.ident.to_string(), |s| s.to_string()); let comments = extract_doc_comments(&self.attrs); - opts.check_used(); - // Check if the enum is a string enum, by checking whether any variant has a string discriminant. let is_string_enum = self.variants.iter().any(|v| { if let Some((_, expr)) = &v.discriminant { @@ -1469,9 +1470,22 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum { false }); if is_string_enum { - return string_enum(self, program, js_name, generate_typescript, comments); + let export_ype = opts.export_type().is_some(); + + opts.check_used(); + + return string_enum( + self, + program, + js_name, + generate_typescript, + export_ype, + comments, + ); } + opts.check_used(); + match self.vis { syn::Visibility::Public(_) => {} _ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"), diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 13b893e4685..83796f185ad 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -109,6 +109,7 @@ macro_rules! shared_api { variant_values: Vec<&'a str>, comments: Vec<&'a str>, generate_typescript: bool, + export_type: bool, } struct Export<'a> { diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index ebc7341bb3c..21d98de7712 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &str = "211103844299778814"; +const APPROVED_SCHEMA_FILE_HASH: &str = "11946883356319465460"; #[test] fn schema_version() { diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 9ef51ede23a..644a2c26257 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -96,6 +96,7 @@ - [`getter` and `setter`](./reference/attributes/on-rust-exports/getter-and-setter.md) - [`inspectable`](./reference/attributes/on-rust-exports/inspectable.md) - [`skip_typescript`](./reference/attributes/on-rust-exports/skip_typescript.md) + - [`export_type`](./reference/attributes/on-rust-exports/export_type.md) - [`getter_with_clone`](./reference/attributes/on-rust-exports/getter_with_clone.md) - [`web-sys`](./web-sys/index.md) diff --git a/guide/src/reference/attributes/on-rust-exports/export_type.md b/guide/src/reference/attributes/on-rust-exports/export_type.md new file mode 100644 index 00000000000..d2474a40b00 --- /dev/null +++ b/guide/src/reference/attributes/on-rust-exports/export_type.md @@ -0,0 +1,84 @@ +# `export_type` + +> **Note:** This attribute is only available in `wasmbindgen` version 0.2.96 and later. + +By default, string enums do not generate any publicly exported runtime bindings and type definitions. This is done because libraries like `web-sys` can use string enums to represent specific JavaScript strings values in DOM APIs (example). If string enums were publicly exported by default, a few dozen string enum type definitions would be added to the public API of all crates that use `web-sys`. + +However, this presents the problem that you cannot make a string enum public if you want to use it in your own public API. For example: + +```rust +// your Rust code + +#[wasm_bindgen] +pub enum Status { + Success = "success", + Failure = "failure", +} + +#[wasm_bindgen] +pub fn get_status() -> Status { + Status::Success +} +``` + +```ts +// the generated TypeScript bindings + +type Status = "success" | "failure"; +export function get_status(): Status; +``` + +As you can see, a type was generated for the `Status` string enum, but the type is not publicly exported. + +While crafty users can work around this by defining their own alias (e.g. as `ReturnType`), the TypeScript API is less ergonomic because `Status` is not directly available. + +The `export_type` attribute can be used to override this behavior and make the string enum publicly exported: + +```rust +// your Rust code + +#[wasm_bindgen(export_type)] +pub enum Status { + Success = "success", + Failure = "failure", +} + +#[wasm_bindgen] +pub fn get_status() -> Status { + Status::Success +} +``` + +```ts +// the generated TypeScript bindings + +export type Status = "success" | "failure"; +export function get_status(): Status; +``` + +## Interaction with `skip_typescript` + +If you use the [`skip_typescript` attribute](./skip_typescript.md) on a string enum, the `export_type` attribute will be ignored. No type definition will be generated for the string enum. + +```rust +// your Rust code + +#[wasm_bindgen(skip_typescript, export_type)] +pub enum Status { + Success = "success", + Failure = "failure", +} + +#[wasm_bindgen] +pub fn get_status() -> Status { + Status::Success +} +``` + +```ts +// the generated TypeScript bindings + +export function get_status(): Status; +``` + +Note that this type definition has a type error, because `Status` is not defined. Using `skip_typescript` almost always requires you to define your own TypeScript types with [`typescript_custom_section`](./typescript_custom_section.md). diff --git a/guide/src/reference/attributes/on-rust-exports/index.md b/guide/src/reference/attributes/on-rust-exports/index.md index f8516caed50..a1a0077fa93 100644 --- a/guide/src/reference/attributes/on-rust-exports/index.md +++ b/guide/src/reference/attributes/on-rust-exports/index.md @@ -1,4 +1,3 @@ # `#[wasm_bindgen]` on Rust Exports -This section enumerates the attributes available for customizing bindings for -Rust functions and `struct`s exported to JavaScript. +This section enumerates the attributes available for customizing bindings for Rust functions, `struct`s, and `enum`s exported to JavaScript.