From b989bc9bfd568bc3b3bba7ac804f797a41f12a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20S=C3=B8holm?= Date: Sat, 26 Feb 2022 09:17:08 +0100 Subject: [PATCH] Add web worker support (#285) * Add data-type attribute to rust links This allows web workers to be included in `index.html`. Since this makes the rust-worker attribute unnecessary it has been removed. Also adds an example showing basic usage and updates the documentation. * Adjust worker file naming and update samples Co-authored-by: Dominik Nakamura --- CHANGELOG.md | 1 + examples/webworker-gloo/Cargo.lock | 317 ++++++++++++++++++++++ examples/webworker-gloo/Cargo.toml | 11 + examples/webworker-gloo/index.html | 14 + examples/webworker-gloo/src/bin/app.rs | 20 ++ examples/webworker-gloo/src/bin/worker.rs | 11 + examples/webworker-gloo/src/lib.rs | 26 ++ examples/webworker/Cargo.lock | 211 ++++++++++++++ examples/webworker/Cargo.toml | 29 ++ examples/webworker/index.html | 14 + examples/webworker/src/bin/app.rs | 50 ++++ examples/webworker/src/bin/worker.rs | 33 +++ site/content/assets.md | 10 +- src/pipelines/html.rs | 11 +- src/pipelines/mod.rs | 12 +- src/pipelines/{rust_app.rs => rust.rs} | 79 +++++- src/pipelines/rust_worker.rs | 59 ---- 17 files changed, 825 insertions(+), 83 deletions(-) create mode 100644 examples/webworker-gloo/Cargo.lock create mode 100644 examples/webworker-gloo/Cargo.toml create mode 100644 examples/webworker-gloo/index.html create mode 100644 examples/webworker-gloo/src/bin/app.rs create mode 100644 examples/webworker-gloo/src/bin/worker.rs create mode 100644 examples/webworker-gloo/src/lib.rs create mode 100644 examples/webworker/Cargo.lock create mode 100644 examples/webworker/Cargo.toml create mode 100644 examples/webworker/index.html create mode 100644 examples/webworker/src/bin/app.rs create mode 100644 examples/webworker/src/bin/worker.rs rename src/pipelines/{rust_app.rs => rust.rs} (86%) delete mode 100644 src/pipelines/rust_worker.rs 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-gloo/Cargo.lock b/examples/webworker-gloo/Cargo.lock new file mode 100644 index 00000000..778884d9 --- /dev/null +++ b/examples/webworker-gloo/Cargo.lock @@ -0,0 +1,317 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[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 = "gloo-console" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3907f786f65bbb4f419e918b0c5674175ef1c231ecda93b2dbd65fd1e8882637" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c77af6f96a4f9e27c8ac23a88407381a31f4a74c3fb985c85aa79b8d898136" +dependencies = [ + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c843b9a46d07485026f030be7bd008580a12a2920fea837745a440a3b0c97cb" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils", + "js-sys", + "serde", + "slab", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +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.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +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.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if 1.0.0", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webworker-gloo" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "gloo-worker", + "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-gloo/Cargo.toml b/examples/webworker-gloo/Cargo.toml new file mode 100644 index 00000000..7e679450 --- /dev/null +++ b/examples/webworker-gloo/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "webworker-gloo" +version = "0.1.0" +edition = "2021" + +[dependencies] +console_error_panic_hook = "0.1.7" +gloo-worker = "0.1.0" +wasm-bindgen = "0.2.79" +web-sys = { version = "0.3.56", features = ["console"] } +wee_alloc = "0.4.5" diff --git a/examples/webworker-gloo/index.html b/examples/webworker-gloo/index.html new file mode 100644 index 00000000..dd88b1ce --- /dev/null +++ b/examples/webworker-gloo/index.html @@ -0,0 +1,14 @@ + + + + + + Trunk | Web worker | Gloo + + + + + + + + \ No newline at end of file diff --git a/examples/webworker-gloo/src/bin/app.rs b/examples/webworker-gloo/src/bin/app.rs new file mode 100644 index 00000000..ba1d99d4 --- /dev/null +++ b/examples/webworker-gloo/src/bin/app.rs @@ -0,0 +1,20 @@ +use std::rc::Rc; + +use gloo_worker::Bridged; +use webworker_gloo::Multiplier; + +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +fn main() { + console_error_panic_hook::set_once(); + + let bridge = Multiplier::bridge(Rc::new(Box::new(|((a, b), result)| { + web_sys::console::log_1(&format!("{a} x {b} = {result}").into()); + }))); + let bridge = Box::leak(bridge); + + bridge.send((2, 5)); + bridge.send((3, 3)); + bridge.send((50, 5)); +} diff --git a/examples/webworker-gloo/src/bin/worker.rs b/examples/webworker-gloo/src/bin/worker.rs new file mode 100644 index 00000000..6c38a2aa --- /dev/null +++ b/examples/webworker-gloo/src/bin/worker.rs @@ -0,0 +1,11 @@ +use gloo_worker::PublicWorker; +use webworker_gloo::Multiplier; + +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +fn main() { + console_error_panic_hook::set_once(); + + Multiplier::register(); +} diff --git a/examples/webworker-gloo/src/lib.rs b/examples/webworker-gloo/src/lib.rs new file mode 100644 index 00000000..9c1ddc58 --- /dev/null +++ b/examples/webworker-gloo/src/lib.rs @@ -0,0 +1,26 @@ +use gloo_worker::{HandlerId, Public, Worker, WorkerLink}; + +pub struct Multiplier { + link: WorkerLink, +} + +impl Worker for Multiplier { + type Input = (u64, u64); + type Message = (); + type Output = ((u64, u64), u64); + type Reach = Public; + + fn create(link: WorkerLink) -> Self { + Self { link } + } + + fn update(&mut self, _msg: Self::Message) {} + + fn handle_input(&mut self, msg: Self::Input, id: HandlerId) { + self.link.respond(id, (msg, msg.0 * msg.1)); + } + + fn name_of_resource() -> &'static str { + "worker.js" + } +} diff --git a/examples/webworker/Cargo.lock b/examples/webworker/Cargo.lock new file mode 100644 index 00000000..57924cc2 --- /dev/null +++ b/examples/webworker/Cargo.lock @@ -0,0 +1,211 @@ +# 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.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +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.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webworker-example" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "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..eaae4ca4 --- /dev/null +++ b/examples/webworker/Cargo.toml @@ -0,0 +1,29 @@ +[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.7" +web-sys = { version = "0.3.56", features = [ + "Blob", + "BlobPropertyBag", + "console", + "MessageEvent", + "Url", + + + "Window", + "Location", + "Document", + "HtmlElement", + "Node", + "Text", + "Worker", + "DedicatedWorkerGlobalScope", +] } +wasm-bindgen = "=0.2.79" +wee_alloc = "0.4.5" +js-sys = "=0.3.56" diff --git a/examples/webworker/index.html b/examples/webworker/index.html new file mode 100644 index 00000000..06280324 --- /dev/null +++ b/examples/webworker/index.html @@ -0,0 +1,14 @@ + + + + + + Trunk | WebWorker | Vanilla + + + + + + + + \ 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..6f04b683 --- /dev/null +++ b/examples/webworker/src/bin/app.rs @@ -0,0 +1,50 @@ +use js_sys::Array; +use wasm_bindgen::{prelude::*, JsCast}; +use web_sys::{window, Blob, BlobPropertyBag, MessageEvent, Url, Worker}; + +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +fn worker_new(name: &str) -> Worker { + let origin = window() + .expect("window to be available") + .location() + .origin() + .expect("origin to be available"); + + let script = Array::new(); + script.push(&format!(r#"importScripts("{origin}/{name}.js");wasm_bindgen("{origin}/{name}_bg.wasm");"#).into()); + + let blob = Blob::new_with_str_sequence_and_options(&script, BlobPropertyBag::new().type_("text/javascript")).expect("blob creation succeeds"); + + let url = Url::create_object_url_with_blob(&blob).expect("url creation succeeds"); + + Worker::new(&url).expect("failed to spawn worker") +} + +fn main() { + console_error_panic_hook::set_once(); + + let worker = worker_new("worker"); + let worker_clone = worker.clone(); + + let onmessage = Closure::wrap(Box::new(move |msg: MessageEvent| { + let worker_clone = worker_clone.clone(); + let data = Array::from(&msg.data()); + + if data.length() == 0 { + let msg = Array::new(); + msg.push(&2.into()); + msg.push(&5.into()); + worker_clone.post_message(&msg.into()).expect("sending message to succeed"); + } else { + let a = data.get(0).as_f64().expect("first array value to be a number") as u32; + let b = data.get(1).as_f64().expect("second array value to be a number") as u32; + let result = data.get(2).as_f64().expect("third array value to be a number") as u32; + + web_sys::console::log_1(&format!("{a} x {b} = {result}").into()); + } + }) as Box); + worker.set_onmessage(Some(onmessage.as_ref().unchecked_ref())); + onmessage.forget(); +} diff --git a/examples/webworker/src/bin/worker.rs b/examples/webworker/src/bin/worker.rs new file mode 100644 index 00000000..71176c19 --- /dev/null +++ b/examples/webworker/src/bin/worker.rs @@ -0,0 +1,33 @@ +use js_sys::Array; +use wasm_bindgen::{prelude::*, JsCast}; +use web_sys::{DedicatedWorkerGlobalScope, MessageEvent}; + +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +fn main() { + console_error_panic_hook::set_once(); + web_sys::console::log_1(&"worker starting".into()); + + let scope = DedicatedWorkerGlobalScope::from(JsValue::from(js_sys::global())); + let scope_clone = scope.clone(); + + let onmessage = Closure::wrap(Box::new(move |msg: MessageEvent| { + web_sys::console::log_1(&"got message".into()); + + let data = Array::from(&msg.data()); + let a = data.get(0).as_f64().expect("first array value to be a number") as u32; + let b = data.get(1).as_f64().expect("second array value to be a number") as u32; + + data.push(&(a * b).into()); + scope_clone + .post_message(&data.into()) + .expect("posting result message succeeds"); + }) as Box); + scope.set_onmessage(Some(onmessage.as_ref().unchecked_ref())); + onmessage.forget(); + + scope + .post_message(&Array::new().into()) + .expect("posting ready message succeeds"); +} 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 86% rename from src/pipelines/rust_app.rs rename to src/pipelines/rust.rs index 80d53302..c9144a76 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,16 +231,23 @@ 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)) } #[tracing::instrument(level = "trace", skip(self, wasm, hashed_name))] async fn wasm_bindgen_build(&self, wasm: &Path, hashed_name: &str) -> Result { + // Skip the hashed file name for workers as their file name must be named at runtime. + // Therefore, workers use the Cargo binary name for file naming. + let hashed_name = match self.app_type { + RustAppType::Main => hashed_name, + RustAppType::Worker => &self.name, + }; + let version = find_wasm_bindgen_version(&self.cfg.tools, &self.manifest); let wasm_bindgen = tools::get(Application::WasmBindgen, version.as_deref()).await?; @@ -225,7 +268,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 +281,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 +290,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 +326,7 @@ impl RustApp { cfg: self.cfg.clone(), js_output: hashed_js_name, wasm_output: hashed_wasm_name, + type_: self.app_type, }) } @@ -366,6 +426,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 +447,15 @@ 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 { + // Skip the script tag and preload links for workers, and remove the link tag only. + // Workers are intialized and managed by the app itself at runtime. + 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!() - } -}