1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2mod core;
3mod files;
4mod transpile_bindgen;
5mod ts_bindgen;
6
7pub mod esm_bindgen;
8pub mod function_bindgen;
9pub mod intrinsics;
10pub mod names;
11pub mod source;
12pub use transpile_bindgen::{AsyncMode, BindingsMode, InstantiationMode, TranspileOpts};
13
14use anyhow::Result;
15use transpile_bindgen::transpile_bindgen;
16
17use anyhow::{bail, ensure, Context};
18use wasmtime_environ::component::{ComponentTypesBuilder, Export, StaticModuleIndex};
19use wasmtime_environ::{PrimaryMap, ScopeVec, Tunables};
20use wit_component::DecodedWasm;
21
22use ts_bindgen::ts_bindgen;
23use wit_parser::{Package, Resolve, Stability, Type, TypeDefKind, TypeId, WorldId};
24
25#[macro_export]
32macro_rules! uwrite {
33 ($dst:expr, $($arg:tt)*) => {
34 write!($dst, $($arg)*).unwrap()
35 };
36}
37
38#[macro_export]
45macro_rules! uwriteln {
46 ($dst:expr, $($arg:tt)*) => {
47 writeln!($dst, $($arg)*).unwrap()
48 };
49}
50
51pub struct Transpiled {
52 pub files: Vec<(String, Vec<u8>)>,
53 pub imports: Vec<String>,
54 pub exports: Vec<(String, Export)>,
55}
56
57pub struct ComponentInfo {
58 pub imports: Vec<String>,
59 pub exports: Vec<(String, wasmtime_environ::component::Export)>,
60}
61
62pub fn generate_types(
63 name: &str,
64 resolve: Resolve,
65 world_id: WorldId,
66 opts: TranspileOpts,
67) -> Result<Vec<(String, Vec<u8>)>, anyhow::Error> {
68 let mut files = files::Files::default();
69
70 ts_bindgen(&name, &resolve, world_id, &opts, &mut files)
71 .context("failed to generate Typescript bindings")?;
72
73 let mut files_out: Vec<(String, Vec<u8>)> = Vec::new();
74 for (name, source) in files.iter() {
75 files_out.push((name.to_string(), source.to_vec()));
76 }
77 Ok(files_out)
78}
79
80#[cfg(feature = "transpile-bindgen")]
83pub fn transpile(component: &[u8], opts: TranspileOpts) -> Result<Transpiled, anyhow::Error> {
84 use wasmtime_environ::component::{Component, Translator};
85
86 let name = opts.name.clone();
87 let mut files = files::Files::default();
88
89 let decoded = wit_component::decode(component)
96 .context("failed to extract interface information from component")?;
97
98 let (resolve, world_id) = match decoded {
99 DecodedWasm::WitPackage(_, _) => bail!("unexpected wit package as input"),
100 DecodedWasm::Component(resolve, world_id) => (resolve, world_id),
101 };
102
103 let scope = ScopeVec::new();
118 let tunables = Tunables::default_u32();
119 let mut validator = wasmtime_environ::wasmparser::Validator::default();
120 let mut types = ComponentTypesBuilder::new(&validator);
121
122 let (component, modules) = Translator::new(&tunables, &mut validator, &mut types, &scope)
123 .translate(component)
124 .context("failed to parse the input component")?;
125
126 let modules: PrimaryMap<StaticModuleIndex, core::Translation<'_>> = modules
127 .into_iter()
128 .map(|(_i, module)| core::Translation::new(module, opts.multi_memory))
129 .collect::<Result<_>>()?;
130
131 let wasmtime_component = Component::default();
132 let types = types.finish(&wasmtime_component);
133
134 for (i, module) in modules.iter() {
137 files.push(&core_file_name(&name, i.as_u32()), module.wasm());
138 }
139
140 if !opts.no_typescript {
141 ts_bindgen(&name, &resolve, world_id, &opts, &mut files)
142 .context("failed to generate Typescript bindings")?;
143 }
144
145 let (imports, exports) = transpile_bindgen(
146 &name, &component, &modules, &types.0, &resolve, world_id, opts, &mut files,
147 );
148
149 let mut files_out: Vec<(String, Vec<u8>)> = Vec::new();
150 for (name, source) in files.iter() {
151 files_out.push((name.to_string(), source.to_vec()));
152 }
153 Ok(Transpiled {
154 files: files_out,
155 imports,
156 exports,
157 })
158}
159
160fn core_file_name(name: &str, idx: u32) -> String {
161 let i_str = if idx == 0 {
162 String::from("")
163 } else {
164 (idx + 1).to_string()
165 };
166 format!("{}.core{i_str}.wasm", name)
167}
168
169pub fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId {
170 loop {
171 match &resolve.types[id].kind {
172 TypeDefKind::Type(Type::Id(that_id)) => id = *that_id,
173 _ => break id,
174 }
175 }
176}
177
178fn feature_gate_allowed(
181 resolve: &Resolve,
182 package: &Package,
183 stability: &Stability,
184 item_name: &str,
185) -> Result<bool> {
186 Ok(match stability {
187 Stability::Unknown => true,
188 Stability::Stable { since, .. } => {
189 let Some(package_version) = package.name.version.as_ref() else {
190 return Ok(true);
193 };
194
195 ensure!(
196 package_version >= since,
197 "feature gate on [{item_name}] refers to an unreleased (future) package version [{since}] (current package version is [{package_version}])"
198 );
199
200 true
202 }
203 Stability::Unstable {
204 feature,
205 deprecated: _,
206 } => {
207 resolve.all_features || resolve.features.contains(feature)
210 }
211 })
212}
213
214pub fn get_thrown_type<'a>(
216 resolve: &'a Resolve,
217 return_type: Option<Type>,
218) -> Option<(Option<&'a Type>, Option<&'a Type>)> {
219 match return_type {
220 None => None,
221 Some(ty) => match ty {
222 Type::Id(id) => match &resolve.types[id].kind {
223 TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
224 _ => None,
225 },
226 _ => None,
227 },
228 }
229}