* Moves index.html creation until after wasm creation so the webserver doesn't prematurely serve the page * sets up nasm for windows builds (to be superseded by Remove Dioxus CLI NASM dependency #2666 #2682) * puts magnanis-cli-support in a blocking task to prevent threadlocking during sequential builds * changes order of client/server builds such that the client is built before the server so the webserver doesn't prematurely serve invalid client code * declobbers "serve" such that each project gets a different dir in the target folder * uses nest_service instead of fallback so the router doesn't respond with fallback codes * reincorporates doge's fix from Fix hot-reloading on Windows #2544
157 lines
6.4 KiB
Rust
157 lines
6.4 KiB
Rust
use crate::cli::serve::Serve;
|
|
use crate::dioxus_crate::DioxusCrate;
|
|
use crate::Result;
|
|
use dioxus_cli_config::Platform;
|
|
use tokio::task::yield_now;
|
|
|
|
mod builder;
|
|
mod hot_reloading_file_map;
|
|
mod logs_tab;
|
|
mod output;
|
|
mod proxy;
|
|
mod server;
|
|
mod watcher;
|
|
|
|
use builder::*;
|
|
use output::*;
|
|
use server::*;
|
|
use watcher::*;
|
|
|
|
/// For *all* builds the CLI spins up a dedicated webserver, file watcher, and build infrastructure to serve the project.
|
|
///
|
|
/// This includes web, desktop, mobile, fullstack, etc.
|
|
///
|
|
/// Platform specifics:
|
|
/// - Web: we need to attach a filesystem server to our devtools webserver to serve the project. We
|
|
/// want to emulate GithubPages here since most folks are deploying there and expect things like
|
|
/// basepath to match.
|
|
/// - Fullstack: We spin up the same dev server but in this case the fullstack server itself needs to
|
|
/// proxy all dev requests to our dev server
|
|
/// - Desktop: We spin up the dev server but without a filesystem server.
|
|
/// - Mobile: Basically the same as desktop.
|
|
///
|
|
/// Notes:
|
|
/// - All filesystem changes are tracked here
|
|
/// - We send all updates to connected websocket connections. Even desktop connects via the websocket
|
|
/// - Right now desktop compiles tokio-tungstenite to do the connection but we could in theory reuse
|
|
/// the websocket logic from the webview for thinner builds.
|
|
///
|
|
/// Todos(Jon):
|
|
/// - I'd love to be able to configure the CLI while it's running so we can change settingaon the fly.
|
|
/// This would require some light refactoring and potentially pulling in something like ratatui.
|
|
/// - Build a custom subscriber for logs by tools within this
|
|
/// - Handle logs from the build engine separately?
|
|
/// - Consume logs from the wasm for web/fullstack
|
|
/// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server
|
|
/// to a dynamic one on the fly.
|
|
pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
|
let mut builder = Builder::new(&dioxus_crate, &serve);
|
|
|
|
// Start the first build
|
|
builder.build();
|
|
|
|
let mut server = Server::start(&serve, &dioxus_crate);
|
|
let mut watcher = Watcher::start(&dioxus_crate);
|
|
let mut screen = Output::start(&serve).expect("Failed to open terminal logger");
|
|
|
|
loop {
|
|
// Make sure we don't hog the CPU: these loop { select! {} } blocks can starve the executor
|
|
yield_now().await;
|
|
|
|
// Draw the state of the server to the screen
|
|
screen.render(&serve, &dioxus_crate, &builder, &server, &watcher);
|
|
|
|
// And then wait for any updates before redrawing
|
|
tokio::select! {
|
|
// rebuild the project or hotreload it
|
|
_ = watcher.wait() => {
|
|
if !watcher.pending_changes() {
|
|
continue;
|
|
}
|
|
|
|
let changed_files = watcher.dequeue_changed_files(&dioxus_crate);
|
|
|
|
// if change is hotreloadable, hotreload it
|
|
// and then send that update to all connected clients
|
|
if let Some(hr) = watcher.attempt_hot_reload(&dioxus_crate, changed_files) {
|
|
// Only send a hotreload message for templates and assets - otherwise we'll just get a full rebuild
|
|
if hr.templates.is_empty() && hr.assets.is_empty() {
|
|
continue
|
|
}
|
|
|
|
server.send_hotreload(hr).await;
|
|
} else {
|
|
// If the change is not binary patchable, rebuild the project
|
|
// We're going to kick off a new build, interrupting the current build if it's ongoing
|
|
builder.build();
|
|
|
|
// Tell the server to show a loading page for any new requests
|
|
server.start_build().await;
|
|
}
|
|
}
|
|
|
|
// reload the page
|
|
msg = server.wait() => {
|
|
// Run the server in the background
|
|
// Waiting for updates here lets us tap into when clients are added/removed
|
|
if let Some(msg) = msg {
|
|
screen.new_ws_message(Platform::Web, msg);
|
|
}
|
|
}
|
|
|
|
// Handle updates from the build engine
|
|
application = builder.wait() => {
|
|
// Wait for logs from the build engine
|
|
// These will cause us to update the screen
|
|
// We also can check the status of the builds here in case we have multiple ongoing builds
|
|
match application {
|
|
Ok(BuilderUpdate::Progress { platform, update }) => {
|
|
screen.new_build_logs(platform, update);
|
|
server.update_build_status(screen.build_progress.progress()).await;
|
|
}
|
|
Ok(BuilderUpdate::Ready { results }) => {
|
|
if !results.is_empty() {
|
|
builder.children.clear();
|
|
}
|
|
|
|
// If we have a build result, open it
|
|
for build_result in results.iter() {
|
|
let child = build_result.open(&serve.server_arguments, server.fullstack_address());
|
|
match child {
|
|
Ok(Some(child_proc)) => builder.children.push((build_result.platform,child_proc)),
|
|
Err(_e) => break,
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
// Make sure we immediately capture the stdout/stderr of the executable -
|
|
// otherwise it'll clobber our terminal output
|
|
screen.new_ready_app(&mut builder, results);
|
|
|
|
// And then finally tell the server to reload
|
|
server.send_reload().await;
|
|
},
|
|
Err(err) => {
|
|
server.send_build_error(err).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle input from the user using our settings
|
|
res = screen.wait() => {
|
|
if res.is_err() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run our cleanup logic here - maybe printing as we go?
|
|
// todo: more printing, logging, error handling in this phase
|
|
_ = screen.shutdown();
|
|
_ = server.shutdown().await;
|
|
builder.shutdown();
|
|
|
|
Ok(())
|
|
}
|