diff --git a/CHANGELOG.md b/CHANGELOG.md
index b066a63f..069c9773 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ Subheadings to categorize changes are `added, changed, deprecated, removed, fixe
### added
- Added the `--address` option for `trunk serve`.
- Open autoreload websocket using wss when assets are served over a secure connection.
+- Added the `data-type` attribute to Rust assets. Can be set to either `main` (previous behaviour and default) or `worker`, which builds the asset and includes it as a web worker.
### changed
- Bump notify to 5.0.0-pre.13, which fixes [notify-rs/notify#356](https://github.com/notify-rs/notify/issues/356)
diff --git a/examples/webworker/Cargo.lock b/examples/webworker/Cargo.lock
new file mode 100644
index 00000000..b4309cd8
--- /dev/null
+++ b/examples/webworker/Cargo.lock
@@ -0,0 +1,210 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bumpalo"
+version = "3.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if 1.0.0",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.112"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+
+[[package]]
+name = "log"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "memory_units"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd"
+dependencies = [
+ "cfg-if 1.0.0",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900"
+dependencies = [
+ "bumpalo",
+ "lazy_static",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
+
+[[package]]
+name = "web-sys"
+version = "0.3.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webworker-example"
+version = "0.1.0"
+dependencies = [
+ "console_error_panic_hook",
+ "wasm-bindgen",
+ "web-sys",
+ "wee_alloc",
+]
+
+[[package]]
+name = "wee_alloc"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "memory_units",
+ "winapi",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/examples/webworker/Cargo.toml b/examples/webworker/Cargo.toml
new file mode 100644
index 00000000..13dadcf8
--- /dev/null
+++ b/examples/webworker/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "webworker-example"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+console_error_panic_hook = "0.1"
+web-sys = { version = "0.3", features = ["Window", "Location", "Document", "HtmlElement", "Node", "Text", "Worker", "DedicatedWorkerGlobalScope"] }
+wasm-bindgen = "=0.2.74"
+wee_alloc = "0.4"
+js-sys = "0.3.55"
diff --git a/examples/webworker/index.html b/examples/webworker/index.html
new file mode 100644
index 00000000..a6798157
--- /dev/null
+++ b/examples/webworker/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Trunk | Web worker
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/webworker/src/bin/app.rs b/examples/webworker/src/bin/app.rs
new file mode 100644
index 00000000..1c281688
--- /dev/null
+++ b/examples/webworker/src/bin/app.rs
@@ -0,0 +1,24 @@
+#![recursion_limit = "1024"]
+
+#[global_allocator]
+static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
+
+use console_error_panic_hook::set_once as set_panic_hook;
+use web_sys::{window, Worker};
+
+fn worker_new(file_name: &str) -> Worker {
+ let origin = window().unwrap().location().origin().unwrap();
+ let url = format!("{}/{}", origin, file_name);
+
+ Worker::new(&url).expect("failed to spawn worker")
+}
+
+fn main() {
+ set_panic_hook();
+ let document = window().and_then(|win| win.document()).expect("Could not access document");
+ let body = document.body().expect("Could not access document.body");
+ let text_node = document.create_text_node("Hello, world from Rust!");
+ body.append_child(text_node.as_ref()).expect("Failed to append text");
+
+ let worker = worker_new("worker.js");
+}
diff --git a/examples/webworker/src/bin/worker.rs b/examples/webworker/src/bin/worker.rs
new file mode 100644
index 00000000..f328e4d9
--- /dev/null
+++ b/examples/webworker/src/bin/worker.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/site/content/assets.md b/site/content/assets.md
index 030ff84e..add813f0 100644
--- a/site/content/assets.md
+++ b/site/content/assets.md
@@ -13,9 +13,10 @@ This will typically look like: ` may be specified"#);
+ let rust_app_nodes = target_html
+ .select(r#"link[data-trunk][rel="rust"][data-type="main"], link[data-trunk][rel="rust"]:not([data-type])"#)
+ .length();
+ ensure!(
+ rust_app_nodes <= 1,
+ r#"only one may be specified"#
+ );
if rust_app_nodes == 0 {
let app = RustApp::new_default(self.cfg.clone(), self.target_html_dir.clone(), self.ignore_chan.clone()).await?;
assets.push(TrunkLink::RustApp(app));
diff --git a/src/pipelines/mod.rs b/src/pipelines/mod.rs
index f193a3e2..14eb58f0 100644
--- a/src/pipelines/mod.rs
+++ b/src/pipelines/mod.rs
@@ -4,8 +4,7 @@ mod css;
mod html;
mod icon;
mod inline;
-mod rust_app;
-mod rust_worker;
+mod rust;
mod sass;
use std::collections::HashMap;
@@ -27,8 +26,7 @@ use crate::pipelines::copy_file::{CopyFile, CopyFileOutput};
use crate::pipelines::css::{Css, CssOutput};
use crate::pipelines::icon::{Icon, IconOutput};
use crate::pipelines::inline::{Inline, InlineOutput};
-use crate::pipelines::rust_app::{RustApp, RustAppOutput};
-use crate::pipelines::rust_worker::{RustWorker, RustWorkerOutput};
+use crate::pipelines::rust::{RustApp, RustAppOutput};
use crate::pipelines::sass::{Sass, SassOutput};
pub use html::HtmlPipeline;
@@ -58,7 +56,6 @@ pub enum TrunkLink {
CopyFile(CopyFile),
CopyDir(CopyDir),
RustApp(RustApp),
- RustWorker(RustWorker),
}
impl TrunkLink {
@@ -77,7 +74,6 @@ impl TrunkLink {
CopyFile::TYPE_COPY_FILE => Self::CopyFile(CopyFile::new(cfg, html_dir, attrs, id).await?),
CopyDir::TYPE_COPY_DIR => Self::CopyDir(CopyDir::new(cfg, html_dir, attrs, id).await?),
RustApp::TYPE_RUST_APP => Self::RustApp(RustApp::new(cfg, html_dir, ignore_chan, attrs, id).await?),
- RustWorker::TYPE_RUST_WORKER => Self::RustWorker(RustWorker::new(cfg, html_dir, ignore_chan, attrs, id).await?),
_ => bail!(
r#"unknown attr value `rel="{}"`; please ensure the value is lowercase and is a supported asset type"#,
rel
@@ -95,7 +91,6 @@ impl TrunkLink {
TrunkLink::CopyFile(inner) => inner.spawn(),
TrunkLink::CopyDir(inner) => inner.spawn(),
TrunkLink::RustApp(inner) => inner.spawn(),
- TrunkLink::RustWorker(inner) => inner.spawn(),
}
}
}
@@ -109,8 +104,6 @@ pub enum TrunkLinkPipelineOutput {
CopyFile(CopyFileOutput),
CopyDir(CopyDirOutput),
RustApp(RustAppOutput),
- #[allow(dead_code)] // TODO: remove this when this pipeline type is implemented.
- RustWorker(RustWorkerOutput),
}
impl TrunkLinkPipelineOutput {
@@ -123,7 +116,6 @@ impl TrunkLinkPipelineOutput {
TrunkLinkPipelineOutput::CopyFile(out) => out.finalize(dom).await,
TrunkLinkPipelineOutput::CopyDir(out) => out.finalize(dom).await,
TrunkLinkPipelineOutput::RustApp(out) => out.finalize(dom).await,
- TrunkLinkPipelineOutput::RustWorker(out) => out.finalize(dom).await,
}
}
}
diff --git a/src/pipelines/rust_app.rs b/src/pipelines/rust.rs
similarity index 88%
rename from src/pipelines/rust_app.rs
rename to src/pipelines/rust.rs
index 80d53302..c9f2f6b1 100644
--- a/src/pipelines/rust_app.rs
+++ b/src/pipelines/rust.rs
@@ -27,6 +27,8 @@ pub struct RustApp {
id: Option,
/// Runtime config.
cfg: Arc,
+ /// Is this module main or a worker.
+ app_type: RustAppType,
/// Space or comma separated list of cargo features to activate.
cargo_features: Option,
/// All metadata associated with the target Cargo project.
@@ -44,6 +46,32 @@ pub struct RustApp {
/// An optional optimization setting that enables wasm-opt. Can be nothing, `0` (default), `1`,
/// `2`, `3`, `4`, `s or `z`. Using `0` disables wasm-opt completely.
wasm_opt: WasmOptLevel,
+ /// Name for the module. Is binary name if given, otherwise it is the name of the cargo project.
+ name: String,
+}
+
+/// Describes how the rust application is used.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum RustAppType {
+ /// Used as the main application.
+ Main,
+ /// Used as a web worker.
+ Worker,
+}
+
+impl FromStr for RustAppType {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> Result {
+ match s {
+ "main" => Ok(RustAppType::Main),
+ "worker" => Ok(RustAppType::Worker),
+ _ => bail!(
+ r#"unknown `data-type="{}"` value for attr; please ensure the value is lowercase and is a supported type"#,
+ s
+ ),
+ }
+ }
}
impl RustApp {
@@ -71,6 +99,7 @@ impl RustApp {
let cargo_features = attrs.get("data-cargo-features").map(|val| val.to_string());
let keep_debug = attrs.contains_key("data-keep-debug");
let no_demangle = attrs.contains_key("data-no-demangle");
+ let app_type = attrs.get("data-type").map(|s| s.as_str()).unwrap_or("main").parse()?;
let wasm_opt = attrs
.get("data-wasm-opt")
.map(|val| val.parse())
@@ -78,6 +107,7 @@ impl RustApp {
.unwrap_or_else(|| if cfg.release { Default::default() } else { WasmOptLevel::Off });
let manifest = CargoMetadata::new(&manifest_href).await?;
let id = Some(id);
+ let name = bin.clone().unwrap_or_else(|| manifest.package.name.clone());
Ok(Self {
id,
@@ -89,12 +119,16 @@ impl RustApp {
keep_debug,
no_demangle,
wasm_opt,
+ app_type,
+ name,
})
}
pub async fn new_default(cfg: Arc, html_dir: Arc, ignore_chan: Option>) -> Result {
let path = html_dir.join("Cargo.toml");
let manifest = CargoMetadata::new(&path).await?;
+ let name = manifest.package.name.clone();
+
Ok(Self {
id: None,
cfg,
@@ -105,6 +139,8 @@ impl RustApp {
keep_debug: false,
no_demangle: false,
wasm_opt: WasmOptLevel::Off,
+ app_type: RustAppType::Main,
+ name,
})
}
@@ -195,11 +231,11 @@ impl RustApp {
.context("could not find WASM output after cargo build")?;
// Hash the built wasm app, then use that as the out-name param.
- tracing::info!("processing WASM");
+ tracing::info!("processing WASM for {}", self.name);
let wasm_bytes = fs::read(&wasm)
.await
.context("error reading wasm file for hash generation")?;
- let hashed_name = format!("index-{:x}", seahash::hash(&wasm_bytes));
+ let hashed_name = format!("{}-{:x}", self.name, seahash::hash(&wasm_bytes));
Ok((wasm.into_std_path_buf(), hashed_name))
}
@@ -225,7 +261,11 @@ impl RustApp {
let arg_out_path = format!("--out-dir={}", bindgen_out);
let arg_out_name = format!("--out-name={}", &hashed_name);
let target_wasm = wasm.to_string_lossy().to_string();
- let mut args = vec!["--target=web", &arg_out_path, &arg_out_name, "--no-typescript", &target_wasm];
+ let target_type = match self.app_type {
+ RustAppType::Main => "--target=web",
+ RustAppType::Worker => "--target=no-modules",
+ };
+ let mut args = vec![target_type, &arg_out_path, &arg_out_name, "--no-typescript", &target_wasm];
if self.keep_debug {
args.push("--keep-debug");
}
@@ -234,7 +274,7 @@ impl RustApp {
}
// Invoke wasm-bindgen.
- tracing::info!("calling wasm-bindgen");
+ tracing::info!("calling wasm-bindgen for {}", self.name);
common::run_command(wasm_bindgen_name, &wasm_bindgen, &args)
.await
.map_err(|err| check_target_not_found_err(err, wasm_bindgen_name))?;
@@ -243,6 +283,18 @@ impl RustApp {
tracing::info!("copying generated wasm-bindgen artifacts");
let hashed_js_name = format!("{}.js", &hashed_name);
let hashed_wasm_name = format!("{}_bg.wasm", &hashed_name);
+ if self.app_type == RustAppType::Worker {
+ let worker_wrapper_path = self.cfg.staging_dist.join(format!("{}.js", self.name));
+ let worker_wrapper = format!(
+ "importScripts('{base}{js}');wasm_bindgen('{base}{wasm}');",
+ base = self.cfg.public_url,
+ js = hashed_js_name,
+ wasm = hashed_wasm_name
+ );
+ fs::write(worker_wrapper_path, worker_wrapper)
+ .await
+ .context("error writing worker wrapper")?;
+ }
let js_loader_path = bindgen_out.join(&hashed_js_name);
let js_loader_path_dist = self.cfg.staging_dist.join(&hashed_js_name);
let wasm_path = bindgen_out.join(&hashed_wasm_name);
@@ -267,6 +319,7 @@ impl RustApp {
cfg: self.cfg.clone(),
js_output: hashed_js_name,
wasm_output: hashed_wasm_name,
+ type_: self.app_type,
})
}
@@ -366,6 +419,8 @@ pub struct RustAppOutput {
pub js_output: String,
/// The filename of the generated WASM file written to the dist dir.
pub wasm_output: String,
+ /// Is this module main or a worker.
+ pub type_: RustAppType,
}
pub fn pattern_evaluate(template: &str, params: &HashMap) -> String {
@@ -385,6 +440,13 @@ pub fn pattern_evaluate(template: &str, params: &HashMap) -> Str
impl RustAppOutput {
pub async fn finalize(self, dom: &mut Document) -> Result<()> {
+ if self.type_ == RustAppType::Worker {
+ if let Some(id) = self.id {
+ dom.select(&super::trunk_id_selector(id)).remove();
+ }
+ return Ok(());
+ }
+
let (base, js, wasm, head, body) = (&self.cfg.public_url, &self.js_output, &self.wasm_output, "html head", "html body");
let (pattern_script, pattern_preload) = (&self.cfg.pattern_script, &self.cfg.pattern_preload);
let mut params: HashMap = match &self.cfg.pattern_params {
diff --git a/src/pipelines/rust_worker.rs b/src/pipelines/rust_worker.rs
deleted file mode 100644
index e7124e34..00000000
--- a/src/pipelines/rust_worker.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-//! Rust web worker pipeline.
-
-#![allow(dead_code, unused_variables)] // TODO: remove this when this pipeline type is implemented.
-
-use std::path::PathBuf;
-use std::sync::Arc;
-
-use anyhow::{bail, Result};
-use nipper::Document;
-use tokio::sync::mpsc;
-use tokio::task::JoinHandle;
-
-use super::{LinkAttrs, TrunkLinkPipelineOutput};
-use crate::config::{CargoMetadata, RtcBuild};
-
-/// A Rust web worker pipeline.
-pub struct RustWorker {
- /// The ID of this pipeline's source HTML element.
- id: usize,
- /// Runtime config.
- cfg: Arc,
- /// All metadata associated with the target Cargo project.
- manifest: CargoMetadata,
- /// An optional channel to be used to communicate ignore paths to the watcher.
- ignore_chan: Option>,
-}
-
-impl RustWorker {
- pub const TYPE_RUST_WORKER: &'static str = "rust-worker";
-
- pub async fn new(
- cfg: Arc, html_dir: Arc, ignore_chan: Option>, attrs: LinkAttrs, id: usize,
- ) -> Result {
- bail!(r#"the rust web worker asset type `` is not yet supported"#)
- }
-
- /// Spawn a new pipeline.
- #[tracing::instrument(level = "trace", skip(self))]
- pub fn spawn(self) -> JoinHandle> {
- unimplemented!()
- }
-}
-
-/// The output of a cargo build pipeline for a Rust web worker.
-pub struct RustWorkerOutput {
- /// The ID of this pipeline.
- pub id: Option,
- pub cfg: Arc,
- /// The filename of the generated JS loader file written to the dist dir.
- pub js_output: String,
- /// The filename of the generated WASM file written to the dist dir.
- pub wasm_output: String,
-}
-
-impl RustWorkerOutput {
- pub async fn finalize(self, dom: &mut Document) -> Result<()> {
- unimplemented!()
- }
-}