mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-12 12:31:22 +00:00
Allow for js property inspection (#1876)
* Add support for #[wasm_bindgen(inspectable)] This annotation generates a `toJSON` and `toString` implementation for generated JavaScript classes which display all readable properties available via the class or its getters This is useful because wasm-bindgen classes currently serialize to display one value named `ptr`, which does not model the properties of the struct in Rust This annotation addresses rustwasm/wasm-bindgen#1857 * Support console.log for inspectable attr in Nodejs `#[wasm_bindgen(inspectable)]` now generates an implementation of `[util.inspect.custom]` for the Node.js target only. This implementation causes `console.log` and friends to yield the same class-style output, but with all readable fields of the Rust struct displayed * Reduce duplication in generated methods Generated `toString` and `[util.inspect.custom]` methods now call `toJSON` to reduce duplication * Store module name in variable
This commit is contained in:
@ -57,6 +57,10 @@ pub struct ExportedClass {
|
||||
typescript: String,
|
||||
has_constructor: bool,
|
||||
wrap_needed: bool,
|
||||
/// Whether to generate helper methods for inspecting the class
|
||||
is_inspectable: bool,
|
||||
/// All readable properties of the class
|
||||
readable_properties: Vec<String>,
|
||||
/// Map from field name to type as a string plus whether it has a setter
|
||||
typescript_fields: HashMap<String, (String, bool)>,
|
||||
}
|
||||
@ -644,6 +648,54 @@ impl<'a> Context<'a> {
|
||||
));
|
||||
}
|
||||
|
||||
// If the class is inspectable, generate `toJSON` and `toString`
|
||||
// to expose all readable properties of the class. Otherwise,
|
||||
// the class shows only the "ptr" property when logged or serialized
|
||||
if class.is_inspectable {
|
||||
// Creates a `toJSON` method which returns an object of all readable properties
|
||||
// This object looks like { a: this.a, b: this.b }
|
||||
dst.push_str(&format!(
|
||||
"
|
||||
toJSON() {{
|
||||
return {{{}}};
|
||||
}}
|
||||
|
||||
toString() {{
|
||||
return JSON.stringify(this);
|
||||
}}
|
||||
",
|
||||
class
|
||||
.readable_properties
|
||||
.iter()
|
||||
.fold(String::from("\n"), |fields, field_name| {
|
||||
format!("{}{name}: this.{name},\n", fields, name = field_name)
|
||||
})
|
||||
));
|
||||
|
||||
if self.config.mode.nodejs() {
|
||||
// `util.inspect` must be imported in Node.js to define [inspect.custom]
|
||||
let module_name = self.import_name(&JsImport {
|
||||
name: JsImportName::Module {
|
||||
module: "util".to_string(),
|
||||
name: "inspect".to_string(),
|
||||
},
|
||||
fields: Vec::new(),
|
||||
})?;
|
||||
|
||||
// Node.js supports a custom inspect function to control the
|
||||
// output of `console.log` and friends. The constructor is set
|
||||
// to display the class name as a typical JavaScript class would
|
||||
dst.push_str(&format!(
|
||||
"
|
||||
[{}.custom]() {{
|
||||
return Object.assign(Object.create({{constructor: this.constructor}}), this.toJSON());
|
||||
}}
|
||||
",
|
||||
module_name
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
dst.push_str(&format!(
|
||||
"
|
||||
free() {{
|
||||
@ -2723,6 +2775,7 @@ impl<'a> Context<'a> {
|
||||
fn generate_struct(&mut self, struct_: &AuxStruct) -> Result<(), Error> {
|
||||
let class = require_class(&mut self.exported_classes, &struct_.name);
|
||||
class.comments = format_doc_comments(&struct_.comments, None);
|
||||
class.is_inspectable = struct_.is_inspectable;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -2975,6 +3028,7 @@ impl ExportedClass {
|
||||
/// generation is handled specially.
|
||||
fn push_getter(&mut self, docs: &str, field: &str, js: &str, ret_ty: &str) {
|
||||
self.push_accessor(docs, field, js, "get ", ret_ty);
|
||||
self.readable_properties.push(field.to_string());
|
||||
}
|
||||
|
||||
/// Used for adding a setter to a class, mainly to ensure that TypeScript
|
||||
|
@ -256,6 +256,8 @@ pub struct AuxStruct {
|
||||
pub name: String,
|
||||
/// The copied Rust comments to forward to JS
|
||||
pub comments: String,
|
||||
/// Whether to generate helper methods for inspecting the class
|
||||
pub is_inspectable: bool,
|
||||
}
|
||||
|
||||
/// All possible types of imports that can be imported by a wasm module.
|
||||
@ -1238,6 +1240,7 @@ impl<'a> Context<'a> {
|
||||
let aux = AuxStruct {
|
||||
name: struct_.name.to_string(),
|
||||
comments: concatenate_comments(&struct_.comments),
|
||||
is_inspectable: struct_.is_inspectable,
|
||||
};
|
||||
self.aux.structs.push(aux);
|
||||
|
||||
|
Reference in New Issue
Block a user