From 0ff1842640553dfefcf0e0b13aee619b17916844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20S=C3=B8holm?= Date: Tue, 14 Dec 2021 01:32:14 +0100 Subject: [PATCH] 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. --- CHANGELOG.md | 1 + site/content/assets.md | 10 ++-- src/pipelines/html.rs | 11 ++-- src/pipelines/mod.rs | 12 +---- src/pipelines/{rust_app.rs => rust.rs} | 73 ++++++++++++++++++++++++-- src/pipelines/rust_worker.rs | 59 --------------------- 6 files changed, 83 insertions(+), 83 deletions(-) rename src/pipelines/{rust_app.rs => rust.rs} (88%) delete mode 100644 src/pipelines/rust_worker.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9001c8a4..1a773701 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/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 ebd4d30f..f6e467fb 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 3abd765e..d4c1996d 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. + 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,10 @@ 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 type_ = attrs + .get("data-type") + .and_then(|s| RustAppType::from_str(s).ok()) + .unwrap_or(RustAppType::Main); let wasm_opt = attrs .get("data-wasm-opt") .map(|val| val.parse()) @@ -78,6 +110,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 +122,16 @@ impl RustApp { keep_debug, no_demangle, wasm_opt, + 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 +142,8 @@ impl RustApp { keep_debug: false, no_demangle: false, wasm_opt: WasmOptLevel::Off, + type_: RustAppType::Main, + name, }) } @@ -195,11 +234,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, hashed_name)) } @@ -225,7 +264,11 @@ impl RustApp { let arg_out_path = format!("--out-dir={}", bindgen_out.display()); 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.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 +277,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 +286,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.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 +322,7 @@ impl RustApp { cfg: self.cfg.clone(), js_output: hashed_js_name, wasm_output: hashed_wasm_name, + type_: self.type_, }) } @@ -366,6 +422,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 +443,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!() - } -}