From 7310483c0e784e55bed015eed8fcf25ca72dd20a Mon Sep 17 00:00:00 2001 From: Calvin Prewitt Date: Fri, 1 Nov 2024 07:35:36 -0500 Subject: [PATCH] added ts for async --- crates/js-component-bindgen/src/ts_bindgen.rs | 75 +++++++++++++++---- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/crates/js-component-bindgen/src/ts_bindgen.rs b/crates/js-component-bindgen/src/ts_bindgen.rs index 8bd22994e..9c1c0a244 100644 --- a/crates/js-component-bindgen/src/ts_bindgen.rs +++ b/crates/js-component-bindgen/src/ts_bindgen.rs @@ -2,7 +2,7 @@ use crate::files::Files; use crate::function_bindgen::{array_ty, as_nullable, maybe_null}; use crate::names::{is_js_identifier, maybe_quote_id, LocalNames, RESERVED_KEYWORDS}; use crate::source::Source; -use crate::transpile_bindgen::{parse_world_key, InstantiationMode, TranspileOpts}; +use crate::transpile_bindgen::{parse_world_key, AsyncMode, InstantiationMode, TranspileOpts}; use crate::{dealias, feature_gate_allowed, uwrite, uwriteln}; use anyhow::{Context as _, Result}; use heck::*; @@ -29,6 +29,9 @@ struct TsBindgen { import_object: Source, /// TypeScript definitions which will become the export object export_object: Source, + + async_imports: HashSet, + async_exports: HashSet, } /// Used to generate a `*.d.ts` file for each imported and exported interface for @@ -53,12 +56,23 @@ pub fn ts_bindgen( opts: &TranspileOpts, files: &mut Files, ) -> Result<()> { + let (async_imports, async_exports) = match opts.async_mode.clone() { + None | Some(AsyncMode::Sync) => (Default::default(), Default::default()), + Some(AsyncMode::JavaScriptPromiseIntegration { imports, exports }) => { + (imports.into_iter().collect(), exports.into_iter().collect()) + } + Some(AsyncMode::Asyncify { imports, exports }) => { + (imports.into_iter().collect(), exports.into_iter().collect()) + } + }; let mut bindgen = TsBindgen { src: Source::default(), interface_names: LocalNames::default(), local_names: LocalNames::default(), import_object: Source::default(), export_object: Source::default(), + async_imports, + async_exports, }; let world = &resolve.worlds[id]; @@ -368,7 +382,7 @@ impl TsBindgen { files: &mut Files, ) -> String { // in case an imported type is used as an exported type - let local_name = self.generate_interface(name, resolve, id, files); + let local_name = self.generate_interface(name, resolve, id, files, false); uwriteln!( self.import_object, "{}: typeof {local_name},", @@ -388,7 +402,7 @@ impl TsBindgen { if iface_name == "*" { uwrite!(self.import_object, "{}: ", maybe_quote_id(import_name)); let name = resolve.interfaces[id].name.as_ref().unwrap(); - let local_name = self.generate_interface(name, resolve, id, files); + let local_name = self.generate_interface(name, resolve, id, files, false); uwriteln!(self.import_object, "typeof {local_name},",); return; } @@ -396,7 +410,7 @@ impl TsBindgen { uwriteln!(self.import_object, "{}: {{", maybe_quote_id(import_name)); for (iface_name, &id) in ifaces { let name = resolve.interfaces[id].name.as_ref().unwrap(); - let local_name = self.generate_interface(name, resolve, id, files); + let local_name = self.generate_interface(name, resolve, id, files, false); uwriteln!( self.import_object, "{}: typeof {local_name},", @@ -415,7 +429,7 @@ impl TsBindgen { ) { uwriteln!(self.import_object, "{}: {{", maybe_quote_id(import_name)); let mut gen = self.ts_interface(resolve, false); - gen.ts_func(func, true, false); + gen.ts_func(func, true, false, false); let src = gen.finish(); self.import_object.push_str(&src); uwriteln!(self.import_object, "}},"); @@ -429,7 +443,7 @@ impl TsBindgen { files: &mut Files, instantiation: bool, ) -> String { - let local_name = self.generate_interface(export_name, resolve, id, files); + let local_name = self.generate_interface(export_name, resolve, id, files, true); if instantiation { uwriteln!( self.export_object, @@ -460,7 +474,7 @@ impl TsBindgen { ) { let mut gen = self.ts_interface(resolve, false); for (_, func) in funcs { - gen.ts_func(func, false, declaration); + gen.ts_func(func, false, declaration, false); } let src = gen.finish(); self.export_object.push_str(&src); @@ -472,6 +486,7 @@ impl TsBindgen { resolve: &Resolve, id: InterfaceId, files: &mut Files, + is_world_export: bool, ) -> String { let iface = resolve .interfaces @@ -520,6 +535,11 @@ impl TsBindgen { return local_name; } + let async_funcs = if is_world_export { + self.async_exports.clone() + } else { + self.async_imports.clone() + }; let mut gen = self.ts_interface(resolve, false); uwriteln!(gen.src, "export namespace {camel} {{"); @@ -530,7 +550,16 @@ impl TsBindgen { { continue; } - gen.ts_func(func, false, true); + let func_name = &func.name; + let is_async = is_world_export && async_funcs.contains(func_name) + || async_funcs.contains(&format!("{id_name}#{func_name}")) + || id_name + .find('@') + .map(|i| { + async_funcs.contains(&format!("{}#{func_name}", id_name.get(0..i).unwrap())) + }) + .unwrap_or(false); + gen.ts_func(func, false, true, is_async); } // Export resources for the interface for (_, ty) in resolve.interfaces[id].types.iter() { @@ -729,7 +758,7 @@ impl<'a> TsInterface<'a> { self.src.push_str("]"); } - fn ts_func(&mut self, func: &Function, default: bool, declaration: bool) { + fn ts_func(&mut self, func: &Function, default: bool, declaration: bool, is_async: bool) { let iface = if let FunctionKind::Method(ty) | FunctionKind::Static(ty) | FunctionKind::Constructor(ty) = func.kind @@ -754,11 +783,15 @@ impl<'a> TsInterface<'a> { func.item_name().to_lower_camel_case() }; + let if_async = if is_async { "async " } else { "" }; + if declaration { match func.kind { FunctionKind::Freestanding => { if is_js_identifier(&out_name) { - iface.src.push_str(&format!("export function {out_name}")); + iface + .src + .push_str(&format!("export {if_async}function {out_name}")); } else { let (local_name, _) = iface.local_names.get_or_create(&out_name, &out_name); iface @@ -766,21 +799,23 @@ impl<'a> TsInterface<'a> { .push_str(&format!("export {{ {local_name} as {out_name} }};\n")); iface .src - .push_str(&format!("declare function {local_name}")); + .push_str(&format!("declare {if_async}function {local_name}")); }; } FunctionKind::Method(_) => { if is_js_identifier(&out_name) { - iface.src.push_str(&out_name); + iface.src.push_str(&format!("{if_async}{out_name}")); } else { - iface.src.push_str(&format!("'{out_name}'")); + iface.src.push_str(&format!("{if_async}'{out_name}'")); } } FunctionKind::Static(_) => { if is_js_identifier(&out_name) { - iface.src.push_str(&format!("static {out_name}")) + iface.src.push_str(&format!("static {if_async}{out_name}")) } else { - iface.src.push_str(&format!("static '{out_name}'")) + iface + .src + .push_str(&format!("static {if_async}'{out_name}'")) } } FunctionKind::Constructor(_) => iface.src.push_str("constructor"), @@ -825,6 +860,10 @@ impl<'a> TsInterface<'a> { } iface.src.push_str(": "); + if is_async { + iface.src.push_str("Promise<"); + } + if let Some((ok_ty, _)) = func.results.throws(iface.resolve) { iface.print_optional_ty(ok_ty); } else { @@ -843,6 +882,12 @@ impl<'a> TsInterface<'a> { } } } + + if is_async { + // closes `Promise<>` + iface.src.push_str(">"); + } + iface.src.push_str(format!("{}\n", end_character).as_str()); }