Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ sha2 = "0.11.0"
base64 = "0.22"
notify = "7"
shell-words = "1.1.1"
wasmtime = "46.0.1"
wasmtime-wasi = "46.0.1"
wasmtime = { version = "46.0.1", optional = true }
wasmtime-wasi = { version = "46.0.1", optional = true }

[features]
default = ["wasm"]
# Sandboxed WASM plugin runtime (wasmtime + wasmtime-wasi). On by default;
# disable with `--no-default-features` to drop the wasmtime dependency tree.
wasm = ["dep:wasmtime", "dep:wasmtime-wasi"]

[build-dependencies]
toml = "0.8"
Expand Down
46 changes: 29 additions & 17 deletions src/plugin/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,25 +646,37 @@ pub(crate) fn install_plugin(
}

if manifest.plugin.is_wasm() {
for cmd in &manifest.commands {
let wasm_path = plugin_dir.join(&cmd.binary);
if wasm_path.exists() {
println!(
" {} Pre-compiling WASM module...",
style("▶").cyan().bold()
);
super::wasm::compile_and_cache(&wasm_path)?;
} else {
remove_plugin_path(&plugin_dir).ok();
bail!(
"WASM binary '{}' not found after build.\n \
Check that the build hook produces a .wasm file at the path declared in plugin.toml.\n \
Expected: {}",
cmd.binary,
wasm_path.display()
);
#[cfg(feature = "wasm")]
{
for cmd in &manifest.commands {
let wasm_path = plugin_dir.join(&cmd.binary);
if wasm_path.exists() {
println!(
" {} Pre-compiling WASM module...",
style("▶").cyan().bold()
);
super::wasm::compile_and_cache(&wasm_path)?;
} else {
remove_plugin_path(&plugin_dir).ok();
bail!(
"WASM binary '{}' not found after build.\n \
Check that the build hook produces a .wasm file at the path declared in plugin.toml.\n \
Expected: {}",
cmd.binary,
wasm_path.display()
);
}
}
}
#[cfg(not(feature = "wasm"))]
{
remove_plugin_path(&plugin_dir).ok();
bail!(
"Plugin '{}' requires the WASM runtime, which was not compiled in \
(rebuild with --features wasm).",
manifest.plugin.name
);
}
}

let command_names = link_commands(&plugin_dir, &bin_dir, &manifest).inspect_err(|_| {
Expand Down
1 change: 1 addition & 0 deletions src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod run_plugin;
mod search;
mod update;
mod validate;
#[cfg(feature = "wasm")]
mod wasm;

#[cfg(test)]
Expand Down
51 changes: 31 additions & 20 deletions src/plugin/run_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,38 @@ pub(super) fn run_plugin_cmd(name: &str, args: &[String]) -> Result<()> {
resolve_protocol_info(name)?
{
if runtime.as_deref() == Some("wasm") {
let manifest_path = plugin_dir.join("plugin.toml");
let content = std::fs::read_to_string(&manifest_path)
.context("reading plugin.toml for WASM plugin")?;
let manifest: PluginManifest =
toml::from_str(&content).context("parsing plugin.toml for WASM plugin")?;
let wasm_binary = manifest
.commands
.iter()
.find(|c| c.name == name)
.or_else(|| manifest.commands.first())
.map(|c| plugin_dir.join(&c.binary))
.ok_or_else(|| anyhow::anyhow!("WASM plugin has no commands defined"))?;
#[cfg(feature = "wasm")]
{
let manifest_path = plugin_dir.join("plugin.toml");
let content = std::fs::read_to_string(&manifest_path)
.context("reading plugin.toml for WASM plugin")?;
let manifest: PluginManifest =
toml::from_str(&content).context("parsing plugin.toml for WASM plugin")?;
let wasm_binary = manifest
.commands
.iter()
.find(|c| c.name == name)
.or_else(|| manifest.commands.first())
.map(|c| plugin_dir.join(&c.binary))
.ok_or_else(|| anyhow::anyhow!("WASM plugin has no commands defined"))?;

return super::wasm::run_wasm_plugin(
&wasm_binary,
args,
&plugin_name,
&plugin_version,
&plugin_dir,
&capabilities,
);
return super::wasm::run_wasm_plugin(
&wasm_binary,
args,
&plugin_name,
&plugin_version,
&plugin_dir,
&capabilities,
);
}
#[cfg(not(feature = "wasm"))]
{
bail!(
"Plugin '{}' requires the WASM runtime, which was not compiled in \
(rebuild with --features wasm).",
name
);
}
}

return crate::protocol::run_protocol_plugin(
Expand Down
10 changes: 10 additions & 0 deletions src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@ mod ui;
#[cfg(test)]
mod tests;

// These re-exports exist solely for the WASM runtime's host bridge
// (`crate::protocol::*` calls in `plugin::wasm`). Within this module the
// handlers are reached via their submodule paths, so when the `wasm` feature
// is disabled the re-exports would be unused — gate them to keep that build
// warning-free.
#[cfg(feature = "wasm")]
pub(crate) use detect::detect_project_context;
#[cfg(feature = "wasm")]
pub(crate) use exec::handle_exec;
#[cfg(feature = "wasm")]
pub(crate) use metadata::handle_metadata;
#[cfg(feature = "wasm")]
pub(crate) use store::{handle_load, handle_store};
#[cfg(feature = "wasm")]
pub(crate) use ui::handle_log;

#[cfg(test)]
Expand Down
Loading