Files
dioxus/packages/cli/src/wasm_opt.rs
T
Jonathan Kelley bdeedc13eb feat: router-based wasm bundle splitting (#3683)
* feat: wasm-split base implementation

* kinda sorta works with the cli

* okay great it actually works

* fix: cache busting for chunks

* cleaned up version

* it works with the router

* wip: migrate to a faster linear solution using id remapping

* add a good harness

* it works completely on the docsite

* the super tight build works too

* make parallelizable

* works on the docsite but not the harness

* light cleanups, fix for both harness and docsite

* make codegen smaller by extract some shared functions to prevent monos

* chunking works, but tbd on tuning heuristics

* extract used module

* wip: cleaning up module

* wip: much better understanding of how imports work

* fix the call graph

* working again...

* wip: thinking about ifuncs

* ifunc approach works

* yes the ifunc approach works!!

* it all works completely even with chunks

* small cleanups

* comments

* actually locate all of the symbols

* clean up final implementation

* clean up tomls, revert some mono changs

* clean up, add some more comments / docs in places

* hoist more packages

* clean up impl a bit more

* clean cli

* move harness to playwright

* update playwright

* extract codgen

* add lazy to components

* Add docs to split loader

* clippy

* typos

* clippy, don't generate splits if not on wasm

* whoops, const

* Typos and docs

* enable optimizations in ci

* add optimizations to ci

* add wasm-split router feature

* parallel playwright?

* fix no case check

* disable pre-compress... think that might be the issue

* better logging, no default pre-compress, caching of shared symbols
* Fix: compressing assets

* fix non-interactive logging for serve

* cache playwright even on failure, use the cli with optimizations

* fix prebuild

* fix playwright webserver
2025-02-13 18:54:38 -08:00

103 lines
3.2 KiB
Rust

use crate::config::WasmOptLevel;
use crate::{Result, WasmOptConfig};
use std::path::Path;
#[memoize::memoize(SharedCache)]
pub fn wasm_opt_available() -> bool {
if cfg!(feature = "optimizations") {
return true;
}
which::which("wasm-opt").is_ok()
}
/// Write these wasm bytes with a particular set of optimizations
pub async fn write_wasm(bytes: &[u8], output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
tokio::fs::write(output_path, bytes).await?;
optimize(output_path, output_path, cfg).await?;
Ok(())
}
#[allow(unreachable_code)]
pub async fn optimize(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
#[cfg(feature = "optimizations")]
return run_from_lib(input_path, output_path, cfg).await;
// It's okay not to run wasm-opt but we should *really* try it
if which::which("wasm-opt").is_err() {
tracing::warn!("wasm-opt not found and CLI is compiled without optimizations. Skipping optimization for {}", input_path.display());
return Ok(());
}
run_locally(input_path, output_path, cfg).await?;
Ok(())
}
async fn run_locally(input_path: &Path, output_path: &Path, cfg: &WasmOptConfig) -> Result<()> {
let mut args = vec![
// needed by wasm-bindgen
"--enable-reference-types",
];
if cfg.memory_packing {
// needed for our current approach to bundle splitting to work properly
// todo(jon): emit the main module's data section in chunks instead of all at once
args.push("--memory-packing");
}
if !cfg.debug {
args.push("--strip-debug");
} else {
args.push("--debuginfo");
}
let level = match cfg.level {
WasmOptLevel::Z => "-Oz",
WasmOptLevel::S => "-Os",
WasmOptLevel::Zero => "-O0",
WasmOptLevel::One => "-O1",
WasmOptLevel::Two => "-O2",
WasmOptLevel::Three => "-O3",
WasmOptLevel::Four => "-O4",
};
tokio::process::Command::new("wasm-opt")
.arg(input_path)
.arg(level)
.arg("-o")
.arg(output_path)
.args(args)
.output()
.await?;
Ok(())
}
/// Use the `wasm_opt` crate
#[cfg(feature = "optimizations")]
async fn run_from_lib(
input_path: &Path,
output_path: &Path,
options: &WasmOptConfig,
) -> Result<()> {
let mut level = match options.level {
WasmOptLevel::Z => wasm_opt::OptimizationOptions::new_optimize_for_size_aggressively(),
WasmOptLevel::S => wasm_opt::OptimizationOptions::new_optimize_for_size(),
WasmOptLevel::Zero => wasm_opt::OptimizationOptions::new_opt_level_0(),
WasmOptLevel::One => wasm_opt::OptimizationOptions::new_opt_level_1(),
WasmOptLevel::Two => wasm_opt::OptimizationOptions::new_opt_level_2(),
WasmOptLevel::Three => wasm_opt::OptimizationOptions::new_opt_level_3(),
WasmOptLevel::Four => wasm_opt::OptimizationOptions::new_opt_level_4(),
};
level
.enable_feature(wasm_opt::Feature::ReferenceTypes)
.add_pass(wasm_opt::Pass::MemoryPacking)
.debug_info(options.debug)
.run(input_path, output_path)
.map_err(|err| crate::Error::Other(anyhow::anyhow!(err)))?;
Ok(())
}