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