Skip to content

Commit f510971

Browse files
committed
Use ServeDir for fullstack dev static assets
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.
1 parent b084432 commit f510971

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

packages/fullstack-server/src/server.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ fn serve_dir_cached<S>(mut router: Router<S>, public_path: &Path, directory: &Pa
407407
where
408408
S: Send + Sync + Clone + 'static,
409409
{
410-
use tower_http::services::ServeFile;
410+
use tower_http::services::{ServeDir, ServeFile};
411411

412412
let dir = std::fs::read_dir(directory)
413413
.unwrap_or_else(|e| panic!("Couldn't read public directory at {:?}: {}", &directory, e));
@@ -430,7 +430,19 @@ where
430430
);
431431

432432
if path.is_dir() {
433-
router = serve_dir_cached(router, public_path, &path);
433+
// In debug builds, serve directories dynamically so new files (like
434+
// wasm patch modules) are immediately visible without rebuilding
435+
// the router. In release, keep the previous cached traversal
436+
// behaviour.
437+
#[cfg(debug_assertions)]
438+
{
439+
router = router.nest_service(&route, ServeDir::new(&path));
440+
}
441+
442+
#[cfg(not(debug_assertions))]
443+
{
444+
router = serve_dir_cached(router, public_path, &path);
445+
}
434446
} else {
435447
let serve_file = ServeFile::new(&path).precompressed_br();
436448
// All cached assets are served at the root of the asset directory. If we know an asset

0 commit comments

Comments
 (0)