Skip to content

Conversation

@tekacs
Copy link
Contributor

@tekacs tekacs commented Nov 18, 2025

Without this patch, simply instantiating the Jumpstart template in fullstack mode and making any hot-patch change results in a 404 on fetch and a client WASM panic.

This changes the fullstack server static asset routing so that, in debug builds, directories under the public tree (including /wasm) are served via tower_http::services::ServeDir instead of being eagerly expanded into a fixed set of routes at startup.

Today dioxus_server::DioxusRouterExt::serve_static_assets walks the public directory once at launch via serve_dir_cached, registering a separate route for every file it sees. That works fine for pre-baked assets, but it breaks down when dx/subsecond writes new files into public during a dev session. In a fullstack web app, wasm hot patches are emitted as timestamped files like public/wasm/lib<name>-patch-<ts>.wasm; the CLI tells the browser to fetch /wasm/lib<name>-patch-<ts>.wasm immediately, but the fullstack server never registered a route for that path, so the fetch returns 404 even though the file already exists on disk.

Non-fullstack dev builds do not have this problem because the dx devserver serves public directly using ServeDir, which consults the filesystem at request time. Fullstack dev, by contrast, proxies asset requests to the inner server, and that server only knows about whatever was in public at the moment its router was built.

This change narrows the gap by making serve_static_assets dynamic for directories in debug builds while preserving the existing behaviour elsewhere:

  • For debug builds (cfg(debug_assertions)), serve_dir_cached now uses ServeDir::new(&path) when it encounters a directory. This means /wasm, /assets, and any other subdirectories are backed by a live directory listing, so new files like wasm patch modules and freshly emitted hashed CSS become immediately visible without restarting the fullstack server or rebuilding its router.
  • For non-debug builds, the previous behaviour is preserved: serve_dir_cached continues to recurse and register one route per discovered file, still using ServeFile::precompressed_br() and cache_response_forever for hashed, cache-busted filenames.

This is a no-op for release builds, but in debug fullstack it's necessary for wasm hot patches and other dynamically-created assets that could 404 until the server happened to be restarted. It also aligns fullstack dev behaviour more closely with the plain web devserver, which already uses ServeDir for the public tree.

Without this patch, simply instantiating the Jumpstart template in fullstack mode and making _any_ hot-patch change would result in a 404 on fetch and a client WASM panic.

This changes the fullstack server static asset routing so that, in debug builds, directories under the `public` tree (including `/wasm`) are served via `tower_http::services::ServeDir` instead of being eagerly expanded into a fixed set of routes at startup.

Today `dioxus_server::DioxusRouterExt::serve_static_assets` walks the `public` directory once at launch via `serve_dir_cached`, registering a separate route for every file it sees. That works fine for pre-baked assets, but it breaks down when dx/subsecond writes new files into `public` during a dev session. In a fullstack web app, wasm hot patches are emitted as timestamped files like `public/wasm/lib<name>-patch-<ts>.wasm`; the CLI tells the browser to fetch `/wasm/lib<name>-patch-<ts>.wasm` immediately, but the fullstack server never registered a route for that path, so the fetch returns 404 even though the file already exists on disk.

Non-fullstack dev builds do not have this problem because the dx devserver serves `public` directly using `ServeDir`, which consults the filesystem at request time. Fullstack dev, by contrast, proxies asset requests to the inner server, and that server only knows about whatever was in `public` at the moment its router was built.

This change narrows the gap by making `serve_static_assets` dynamic for directories in debug builds while preserving the existing behaviour elsewhere:

- For debug builds (`cfg(debug_assertions)`), `serve_dir_cached` now uses `ServeDir::new(&path)` when it encounters a directory. This means `/wasm`, `/assets`, and any other subdirectories are backed by a live directory listing, so new files like wasm patch modules and freshly emitted hashed CSS become immediately visible without restarting the fullstack server or rebuilding its router.
- For non-debug builds, the previous behaviour is preserved: `serve_dir_cached` continues to recurse and register one route per discovered file, still using `ServeFile::precompressed_br()` and `cache_response_forever` for hashed, cache-busted filenames.

This is a no-op for release builds, but in debug fullstack it's necessary for wasm hot patches and other dynamically-created assets that could 404 until the server happened to be restarted. It also aligns fullstack dev behaviour more closely with the plain web devserver, which already uses `ServeDir` for the `public` tree.
@tekacs tekacs requested a review from a team as a code owner November 18, 2025 05:40
@tekacs tekacs force-pushed the fix-fullstack-hot-patching-with-servedir branch 2 times, most recently from 88110a7 to f510971 Compare November 18, 2025 06:03
@tekacs
Copy link
Contributor Author

tekacs commented Nov 18, 2025

As a total aside – fullstack is slick! Thanks so much for setting this up, it feels great to use!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant