diff --git a/crates/js-component-bindgen-component/src/lib.rs b/crates/js-component-bindgen-component/src/lib.rs index e16bd44dc..66da757d9 100644 --- a/crates/js-component-bindgen-component/src/lib.rs +++ b/crates/js-component-bindgen-component/src/lib.rs @@ -75,6 +75,7 @@ impl Guest for JsComponentBindgenComponent { no_namespaced_exports: options.no_namespaced_exports.unwrap_or(false), multi_memory: options.multi_memory.unwrap_or(false), import_bindings: options.import_bindings.map(Into::into), + declare_imports: options.declare_imports.unwrap_or(false), }; let js_component_bindgen::Transpiled { @@ -160,6 +161,7 @@ impl Guest for JsComponentBindgenComponent { no_namespaced_exports: false, multi_memory: false, import_bindings: None, + declare_imports: false, }; let files = generate_types(name, resolve, world, opts).map_err(|e| e.to_string())?; diff --git a/crates/js-component-bindgen-component/wit/js-component-bindgen.wit b/crates/js-component-bindgen-component/wit/js-component-bindgen.wit index 631e5deef..cf7252d56 100644 --- a/crates/js-component-bindgen-component/wit/js-component-bindgen.wit +++ b/crates/js-component-bindgen-component/wit/js-component-bindgen.wit @@ -58,6 +58,9 @@ world js-component-bindgen { /// Whether to generate namespaced exports like `foo as "local:package/foo"`. /// These exports can break typescript builds. no-namespaced-exports: option, + + /// Whether to generate module declarations like `declare module "local:package/foo" {...`. + declare-imports: option, /// Whether to output core Wasm utilizing multi-memory or to polyfill /// this handling. @@ -91,6 +94,8 @@ world js-component-bindgen { map: option, /// Features that should be enabled as part of feature gating features: option, + /// Whether to generate module declarations like `declare module "local:package/foo" {...`. + declare-imports: option, } enum export-type { diff --git a/crates/js-component-bindgen/src/transpile_bindgen.rs b/crates/js-component-bindgen/src/transpile_bindgen.rs index 7f13c6997..0f4783d83 100644 --- a/crates/js-component-bindgen/src/transpile_bindgen.rs +++ b/crates/js-component-bindgen/src/transpile_bindgen.rs @@ -68,6 +68,8 @@ pub struct TranspileOpts { /// Whether to output core Wasm utilizing multi-memory or to polyfill /// this handling. pub multi_memory: bool, + /// Whether to generate module declarations like `declare module "local:package/foo" {...`. + pub declare_imports: bool, } #[derive(Default, Clone, Debug)] diff --git a/crates/js-component-bindgen/src/ts_bindgen.rs b/crates/js-component-bindgen/src/ts_bindgen.rs index bd250fc92..bee9e9eda 100644 --- a/crates/js-component-bindgen/src/ts_bindgen.rs +++ b/crates/js-component-bindgen/src/ts_bindgen.rs @@ -219,6 +219,7 @@ pub fn ts_bindgen( *id, files, opts.instantiation.is_some(), + opts.declare_imports, ); export_aliases.push((iface_name.to_lower_camel_case(), local_name)); } @@ -368,7 +369,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 +389,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 +397,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},", @@ -428,8 +429,9 @@ impl TsBindgen { id: InterfaceId, files: &mut Files, instantiation: bool, + declare_imports: bool, ) -> String { - let local_name = self.generate_interface(export_name, resolve, id, files); + let local_name = self.generate_interface(export_name, resolve, id, files, declare_imports); if instantiation { uwriteln!( self.export_object, @@ -472,6 +474,7 @@ impl TsBindgen { resolve: &Resolve, id: InterfaceId, files: &mut Files, + declare_imports: bool, ) -> String { let iface = resolve .interfaces @@ -522,7 +525,15 @@ impl TsBindgen { let mut gen = self.ts_interface(resolve, false); - uwriteln!(gen.src, "export namespace {camel} {{"); + uwriteln!( + gen.src, + "{}", + if declare_imports { + format!("declare module '{id_name}' {{") + } else { + format!("export namespace {camel} {{") + } + ); for (_, func) in resolve.interfaces[id].functions.iter() { // Ensure that the function the world item for stability guarantees and exclude if they do not match if !feature_gate_allowed(resolve, package, &func.stability, &func.name) diff --git a/src/jco.js b/src/jco.js index 99ef7cde6..0cb81f16f 100755 --- a/src/jco.js +++ b/src/jco.js @@ -79,6 +79,7 @@ program.command('types') .option('-q, --quiet', 'disable output summary') .option('--feature ', 'enable one specific WIT feature (repeatable)', collectOptions, []) .option('--all-features', 'enable all features') + .option('--declare-imports', 'declare imports as modules in the generated types') .action(asyncAction(types)); program.command('run') diff --git a/xtask/src/build/jco.rs b/xtask/src/build/jco.rs index 8f2577eae..6ea18e35b 100644 --- a/xtask/src/build/jco.rs +++ b/xtask/src/build/jco.rs @@ -84,6 +84,7 @@ fn transpile(component_path: &str, name: String, optimize: bool) -> Result<()> { no_namespaced_exports: true, multi_memory: true, import_bindings: Some(BindingsMode::Js), + declare_imports: false, }; let transpiled = js_component_bindgen::transpile(&adapted_component, opts)?; diff --git a/xtask/src/generate/wasi_types.rs b/xtask/src/generate/wasi_types.rs index f4942d06c..61f2d2b51 100644 --- a/xtask/src/generate/wasi_types.rs +++ b/xtask/src/generate/wasi_types.rs @@ -38,6 +38,7 @@ pub(crate) fn run() -> Result<()> { no_namespaced_exports: true, multi_memory: false, import_bindings: Some(BindingsMode::Js), + declare_imports: false, }; let files = generate_types(name, resolve, world, opts)?;