diff --git a/src/pipelines/rust/initializer.js b/src/pipelines/rust/initializer.js index 86dc7f89..1a7ba223 100644 --- a/src/pipelines/rust/initializer.js +++ b/src/pipelines/rust/initializer.js @@ -1,13 +1,8 @@ -async function __trunkInitializer(init, source, sourceSize, initializer) { +async function __trunkInitializer(init, source, sourceSize, initializer, initWithObject) { if (initializer === undefined) { - return await init(source); + return await init(initWithObject ? { module_or_path: source } : source); } - return await __trunkInitWithProgress(init, source, sourceSize, initializer); -} - -async function __trunkInitWithProgress(init, source, sourceSize, initializer) { - const { onStart, onProgress, onComplete, onSuccess, onFailure } = initializer; @@ -55,7 +50,7 @@ async function __trunkInitWithProgress(init, source, sourceSize, initializer) { new Response(stream, init), ); - return init(response) + return init(initWithObject ? { module_or_path: response } : response) .then((value) => { onComplete?.(); onSuccess?.(value); diff --git a/src/pipelines/rust/mod.rs b/src/pipelines/rust/mod.rs index 6c88c712..92f09a05 100644 --- a/src/pipelines/rust/mod.rs +++ b/src/pipelines/rust/mod.rs @@ -20,7 +20,7 @@ use crate::{ }, pipelines::rust::sri::{SriBuilder, SriOptions, SriType}, processing::{integrity::IntegrityType, minify::minify_js}, - tools::{self, Application}, + tools::{self, Application, ToolInformation}, }; use anyhow::{anyhow, bail, ensure, Context, Result}; use cargo_metadata::Artifact; @@ -36,7 +36,7 @@ use std::{ }; use tokio::{fs, io::AsyncWriteExt, process::Command, sync::mpsc, task::JoinHandle}; use tracing::log; -use wasm_bindgen::{find_wasm_bindgen_version, WasmBindgenTarget}; +use wasm_bindgen::{find_wasm_bindgen_version, WasmBindgenFeatures, WasmBindgenTarget}; use wasm_opt::WasmOptLevel; /// A Rust application pipeline. @@ -523,13 +523,17 @@ impl RustApp { #[tracing::instrument(level = "trace", skip(self))] async fn wasm_bindgen_build(&mut self, wasm_path: &Path) -> Result { let version = find_wasm_bindgen_version(&self.cfg.tools, &self.manifest); - let wasm_bindgen = tools::get( + let ToolInformation { + path: wasm_bindgen, + version, + } = tools::get_info( Application::WasmBindgen, version.as_deref(), self.cfg.offline, &self.cfg.client_options(), ) .await?; + let wasm_bindgen_features = WasmBindgenFeatures::from_version(&version)?; // Ensure our output dir is in place. let wasm_bindgen_name = Application::WasmBindgen.name(); @@ -747,6 +751,7 @@ impl RustApp { import_bindings: self.import_bindings, import_bindings_name: self.import_bindings_name.clone(), initializer, + wasm_bindgen_features, }) } diff --git a/src/pipelines/rust/output.rs b/src/pipelines/rust/output.rs index 5fe9b58d..64778db0 100644 --- a/src/pipelines/rust/output.rs +++ b/src/pipelines/rust/output.rs @@ -2,7 +2,7 @@ use super::super::trunk_id_selector; use crate::{ common::{html_rewrite::Document, nonce}, config::{rt::RtcBuild, types::CrossOrigin}, - pipelines::rust::{sri::SriBuilder, RustAppType}, + pipelines::rust::{sri::SriBuilder, wasm_bindgen::WasmBindgenFeatures, RustAppType}, }; use anyhow::bail; use std::{collections::HashMap, sync::Arc}; @@ -31,6 +31,8 @@ pub struct RustAppOutput { pub import_bindings_name: Option, /// The target of the initializer module pub initializer: Option, + /// The features supported by the version of wasm-bindgen used + pub wasm_bindgen_features: WasmBindgenFeatures, } pub fn pattern_evaluate(template: &str, params: &HashMap) -> String { @@ -133,16 +135,23 @@ window.{bindings} = bindings; dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}})); "#; + let init_with_object = self.wasm_bindgen_features.init_with_object; + match &self.initializer { None => format!( r#" "# +"#, + init_arg = if init_with_object { + format!("{{ module_or_path: '{base}{wasm}' }}") + } else { + format!("'{base}{wasm}'") + } ), Some(initializer) => format!( r#" @@ -152,7 +161,7 @@ const wasm = await init('{base}{wasm}'); import init{import} from '{base}{js}'; import initializer from '{base}{initializer}'; -const wasm = await __trunkInitializer(init, '{base}{wasm}', {size}, initializer()); +const wasm = await __trunkInitializer(init, '{base}{wasm}', {size}, initializer(), {init_with_object}); {bind} {fire} diff --git a/src/pipelines/rust/wasm_bindgen.rs b/src/pipelines/rust/wasm_bindgen.rs index 00fb8995..74858bce 100644 --- a/src/pipelines/rust/wasm_bindgen.rs +++ b/src/pipelines/rust/wasm_bindgen.rs @@ -1,6 +1,7 @@ use crate::config::{CargoMetadata, Tools}; -use anyhow::anyhow; +use anyhow::{anyhow, Context}; use cargo_lock::Lockfile; +use semver::{Comparator, Op, Prerelease, Version}; use std::borrow::Cow; use std::fmt::{Display, Formatter}; use std::path::Path; @@ -87,3 +88,32 @@ pub fn find_wasm_bindgen_version<'a>( .or_else(find_lock) .or_else(find_manifest) } + +/// Features supported by a certain version of wasm-bindgen. +pub struct WasmBindgenFeatures { + /// Whether we can and should pass an object to the initialization function. + /// + /// In wasm-bindgen 0.2.93, parameters to `init` were deprecated in favor of + /// an object (see [wasm-bindgen#3995]). From this version onward, wrap the + /// arguments in an object and pass the object instead. + /// + /// [wasm-bindgen#3995]: https://github.com/rustwasm/wasm-bindgen/pull/3995 + pub init_with_object: bool, +} + +const VERSION_GE_0_2_93: Comparator = Comparator { + op: Op::GreaterEq, + major: 0, + minor: Some(2), + patch: Some(93), + pre: Prerelease::EMPTY, +}; + +impl WasmBindgenFeatures { + pub fn from_version(version: &str) -> Result { + let version = Version::parse(version).context("error parsing wasm-bindgen version")?; + Ok(Self { + init_with_object: VERSION_GE_0_2_93.matches(&version), + }) + } +} diff --git a/src/tools.rs b/src/tools.rs index 0974148b..73ed5f31 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -246,6 +246,14 @@ impl AppCache { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ToolInformation { + /// The path to the tool's binary + pub path: PathBuf, + /// The version of the tool + pub version: String, +} + /// Locate the given application and download it if missing. #[tracing::instrument(level = "debug")] pub async fn get( @@ -254,6 +262,17 @@ pub async fn get( offline: bool, client_options: &HttpClientOptions, ) -> Result { + Ok(get_info(app, version, offline, client_options).await?.path) +} + +/// Locate the given application and download it if missing, returning detailed information. +#[tracing::instrument(level = "debug")] +pub async fn get_info( + app: Application, + version: Option<&str>, + offline: bool, + client_options: &HttpClientOptions, +) -> Result { tracing::debug!("Getting tool"); if let Some((path, detected_version)) = find_system(app).await { @@ -264,7 +283,10 @@ pub async fn get( if required_version == detected_version { // and a match, so return early tracing::debug!(%detected_version, "using system installed binary: {}", path.display()); - return Ok(path); + return Ok(ToolInformation { + path, + version: detected_version, + }); } else if offline { // a mismatch, in offline mode, we can't help here bail!( @@ -277,7 +299,10 @@ pub async fn get( } } else { // we don't require any specific version - return Ok(path); + return Ok(ToolInformation { + path, + version: detected_version, + }); } } @@ -308,7 +333,10 @@ pub async fn get( bin_path.display() ); - Ok(bin_path) + Ok(ToolInformation { + path: bin_path, + version: version.to_owned(), + }) } /// Try to find a global system installed version of the application.