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
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-02-13 18:54:38 -08:00
1 parent 14eb7597df
commit bdeedc13eb
59 files changed
+4249 -299

No files matched your search

+19 -11
View File
@@ -75,19 +75,27 @@ impl<F: ComponentFunction<P, M> + Clone, P: Clone + 'static, M: 'static> AnyProp
}
fn render(&self) -> Element {
let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
self.render_fn.rebuild(self.props.clone())
}));
match res {
Ok(node) => node,
Err(err) => {
let component_name = self.name;
tracing::error!("Panic while rendering component `{component_name}`: {err:?}");
let panic = CapturedPanic { error: err };
Element::Err(panic.into())
fn render_inner(name: &str, res: Result<Element, Box<dyn Any + Send>>) -> Element {
match res {
Ok(node) => node,
Err(err) => {
// on wasm this massively bloats binary sizes and we can't even capture the panic
// so do nothing
#[cfg(not(target_arch = "wasm32"))]
{
tracing::error!("Panic while rendering component `{name}`: {err:?}");
}
Element::Err(CapturedPanic { error: err }.into())
}
}
}
render_inner(
self.name,
std::panic::catch_unwind(AssertUnwindSafe(move || {
self.render_fn.rebuild(self.props.clone())
})),
)
}
fn duplicate(&self) -> BoxedAnyProps {
+10
View File
@@ -450,6 +450,16 @@ impl Scope {
hooks.push(Box::new(initializer()));
}
self.use_hook_inner::<State>(hooks, cur_hook)
}
// The interior version that gets monoorphized by the `State` type but not the `initializer` type.
// This helps trim down binary sizes
fn use_hook_inner<State: Clone + 'static>(
&self,
hooks: std::cell::RefMut<Vec<Box<dyn std::any::Any>>>,
cur_hook: usize,
) -> State {
hooks
.get(cur_hook)
.and_then(|inn| {
+11 -1
View File
@@ -162,6 +162,16 @@ impl Runtime {
scope: ScopeId,
task: impl Future<Output = ()> + 'static,
ty: TaskType,
) -> Task {
self.spawn_task_of_type_inner(scope, Box::pin(task), ty)
}
// a non-momorphic version of spawn_task_of_type, helps with binari sizes
fn spawn_task_of_type_inner(
&self,
scope: ScopeId,
pinned_task: Pin<Box<dyn Future<Output = ()>>>,
ty: TaskType,
) -> Task {
// Insert the task, temporarily holding a borrow on the tasks map
let (task, task_id) = {
@@ -176,7 +186,7 @@ impl Runtime {
scope,
active: Cell::new(true),
parent: self.current_task(),
task: RefCell::new(Box::pin(task)),
task: RefCell::new(pinned_task),
waker: futures_util::task::waker(Arc::new(LocalTaskHandle {
id: task_id.id,
tx: self.sender.clone(),