feat: add dev-server
This commit is contained in:
1 parent
281a075c6c
commit
5e710f6693
11 files changed
+307
-148
No files matched your search
@@ -13,8 +13,7 @@ pub struct Build {
|
||||
|
||||
impl Build {
|
||||
pub fn build(self) -> anyhow::Result<()> {
|
||||
|
||||
let mut crate_config = crate::CrateConfig::new()?;
|
||||
let mut crate_config = crate::CrateConfig::new()?;
|
||||
|
||||
// change the relase state.
|
||||
crate_config.with_release(self.build.release);
|
||||
@@ -23,4 +22,4 @@ impl Build {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,26 @@ pub struct ConfigOptsBuild {
|
||||
pub pattern_params: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, StructOpt)]
|
||||
pub struct ConfigOptsServe {
|
||||
/// The index HTML file to drive the bundling process [default: index.html]
|
||||
#[structopt(parse(from_os_str))]
|
||||
pub target: Option<PathBuf>,
|
||||
|
||||
/// Build in release mode [default: false]
|
||||
#[structopt(long)]
|
||||
#[serde(default)]
|
||||
pub release: bool,
|
||||
|
||||
/// The output dir for all final assets [default: dist]
|
||||
#[structopt(short, long, parse(from_os_str))]
|
||||
pub dist: Option<PathBuf>,
|
||||
|
||||
/// The public URL from which assets are to be served [default: /]
|
||||
#[structopt(long, parse(from_str=parse_public_url))]
|
||||
pub public_url: Option<String>,
|
||||
}
|
||||
|
||||
/// Ensure the given value for `--public-url` is formatted correctly.
|
||||
pub fn parse_public_url(val: &str) -> String {
|
||||
let prefix = if !val.starts_with('/') { "/" } else { "" };
|
||||
|
||||
@@ -35,7 +35,6 @@ pub enum Commands {
|
||||
Translate(translate::Translate),
|
||||
// /// Build, watch & serve the Rust WASM app and all of its assets.
|
||||
Serve(serve::Serve),
|
||||
|
||||
// /// Clean output artifacts.
|
||||
// Clean(clean::Clean),
|
||||
|
||||
|
||||
+118
-118
@@ -1,145 +1,145 @@
|
||||
use crate::{cli::DevelopOptions, config::CrateConfig, error::Result};
|
||||
use async_std::prelude::FutureExt;
|
||||
// use crate::{cli::DevelopOptions, config::CrateConfig, error::Result};
|
||||
// use async_std::prelude::FutureExt;
|
||||
|
||||
use log::info;
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use tide::http::mime::HTML;
|
||||
use tide::http::Mime;
|
||||
// use log::info;
|
||||
// use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
// use std::path::PathBuf;
|
||||
// use std::sync::atomic::AtomicBool;
|
||||
// use std::sync::Arc;
|
||||
// use tide::http::mime::HTML;
|
||||
// use tide::http::Mime;
|
||||
|
||||
pub struct DevelopState {
|
||||
//
|
||||
reload_on_change: bool,
|
||||
}
|
||||
// pub struct DevelopState {
|
||||
// //
|
||||
// reload_on_change: bool,
|
||||
// }
|
||||
|
||||
pub async fn develop(options: DevelopOptions) -> Result<()> {
|
||||
//
|
||||
log::info!("Starting development server 🚀");
|
||||
let mut cfg = CrateConfig::new()?;
|
||||
cfg.with_develop_options(&options);
|
||||
// pub async fn develop(options: DevelopOptions) -> Result<()> {
|
||||
// //
|
||||
// log::info!("Starting development server 🚀");
|
||||
// let mut cfg = CrateConfig::new()?;
|
||||
// cfg.with_develop_options(&options);
|
||||
|
||||
let out_dir = cfg.out_dir.clone();
|
||||
// let out_dir = cfg.out_dir.clone();
|
||||
|
||||
let is_err = Arc::new(AtomicBool::new(false));
|
||||
// let is_err = Arc::new(AtomicBool::new(false));
|
||||
|
||||
// Spawn the server onto a seperate task
|
||||
// This lets the task progress while we handle file updates
|
||||
let server = async_std::task::spawn(launch_server(out_dir, is_err.clone()));
|
||||
let watcher = async_std::task::spawn(watch_directory(cfg.clone(), is_err.clone()));
|
||||
// // Spawn the server onto a seperate task
|
||||
// // This lets the task progress while we handle file updates
|
||||
// let server = async_std::task::spawn(launch_server(out_dir, is_err.clone()));
|
||||
// let watcher = async_std::task::spawn(watch_directory(cfg.clone(), is_err.clone()));
|
||||
|
||||
match server.race(watcher).await {
|
||||
Err(e) => log::warn!("Error running development server, {:?}", e),
|
||||
_ => {}
|
||||
}
|
||||
// match server.race(watcher).await {
|
||||
// Err(e) => log::warn!("Error running development server, {:?}", e),
|
||||
// _ => {}
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
async fn watch_directory(config: CrateConfig, is_err: ErrStatus) -> Result<()> {
|
||||
// Create a channel to receive the events.
|
||||
let (watcher_tx, watcher_rx) = async_std::channel::bounded(100);
|
||||
// async fn watch_directory(config: CrateConfig, is_err: ErrStatus) -> Result<()> {
|
||||
// // Create a channel to receive the events.
|
||||
// let (watcher_tx, watcher_rx) = async_std::channel::bounded(100);
|
||||
|
||||
// Automatically select the best implementation for your platform.
|
||||
// You can also access each implementation directly e.g. INotifyWatcher.
|
||||
let mut watcher: RecommendedWatcher = Watcher::new(move |res| {
|
||||
async_std::task::block_on(watcher_tx.send(res));
|
||||
// send an event
|
||||
let _ = async_std::task::block_on(watcher_tx.send(res));
|
||||
})
|
||||
.expect("failed to make watcher");
|
||||
// // Automatically select the best implementation for your platform.
|
||||
// // You can also access each implementation directly e.g. INotifyWatcher.
|
||||
// let mut watcher: RecommendedWatcher = Watcher::new(move |res| {
|
||||
// async_std::task::block_on(watcher_tx.send(res));
|
||||
// // send an event
|
||||
// let _ = async_std::task::block_on(watcher_tx.send(res));
|
||||
// })
|
||||
// .expect("failed to make watcher");
|
||||
|
||||
let src_dir = crate::cargo::crate_root()?;
|
||||
// let src_dir = crate::cargo::crate_root()?;
|
||||
|
||||
// Add a path to be watched. All files and directories at that path and
|
||||
// below will be monitored for changes.
|
||||
watcher
|
||||
.watch(&src_dir.join("src"), RecursiveMode::Recursive)
|
||||
.expect("Failed to watch dir");
|
||||
// // Add a path to be watched. All files and directories at that path and
|
||||
// // below will be monitored for changes.
|
||||
// watcher
|
||||
// .watch(&src_dir.join("src"), RecursiveMode::Recursive)
|
||||
// .expect("Failed to watch dir");
|
||||
|
||||
match watcher.watch(&src_dir.join("examples"), RecursiveMode::Recursive) {
|
||||
Ok(_) => {}
|
||||
Err(e) => log::warn!("Failed to watch examples dir, {:?}", e),
|
||||
}
|
||||
// match watcher.watch(&src_dir.join("examples"), RecursiveMode::Recursive) {
|
||||
// Ok(_) => {}
|
||||
// Err(e) => log::warn!("Failed to watch examples dir, {:?}", e),
|
||||
// }
|
||||
|
||||
'run: loop {
|
||||
match crate::builder::build(&config) {
|
||||
Ok(_) => {
|
||||
is_err.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
async_std::task::sleep(std::time::Duration::from_millis(500)).await;
|
||||
}
|
||||
Err(err) => is_err.store(true, std::sync::atomic::Ordering::Relaxed),
|
||||
};
|
||||
// 'run: loop {
|
||||
// match crate::builder::build(&config) {
|
||||
// Ok(_) => {
|
||||
// is_err.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
// async_std::task::sleep(std::time::Duration::from_millis(500)).await;
|
||||
// }
|
||||
// Err(err) => is_err.store(true, std::sync::atomic::Ordering::Relaxed),
|
||||
// };
|
||||
|
||||
let mut msg = None;
|
||||
loop {
|
||||
let new_msg = watcher_rx.recv().await.unwrap().unwrap();
|
||||
if !watcher_rx.is_empty() {
|
||||
msg = Some(new_msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// let mut msg = None;
|
||||
// loop {
|
||||
// let new_msg = watcher_rx.recv().await.unwrap().unwrap();
|
||||
// if !watcher_rx.is_empty() {
|
||||
// msg = Some(new_msg);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
info!("File updated, rebuilding app");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
// info!("File updated, rebuilding app");
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
async fn launch_server(outdir: PathBuf, is_err: ErrStatus) -> Result<()> {
|
||||
let _crate_dir = crate::cargo::crate_root()?;
|
||||
let _workspace_dir = crate::cargo::workspace_root()?;
|
||||
// async fn launch_server(outdir: PathBuf, is_err: ErrStatus) -> Result<()> {
|
||||
// let _crate_dir = crate::cargo::crate_root()?;
|
||||
// let _workspace_dir = crate::cargo::workspace_root()?;
|
||||
|
||||
let mut app = tide::with_state(ServerState::new(outdir.to_owned(), is_err));
|
||||
// let mut app = tide::with_state(ServerState::new(outdir.to_owned(), is_err));
|
||||
|
||||
let file_path = format!("{}/index.html", outdir.display());
|
||||
log::info!("Serving {}", file_path);
|
||||
let p = outdir.display().to_string();
|
||||
// let file_path = format!("{}/index.html", outdir.display());
|
||||
// log::info!("Serving {}", file_path);
|
||||
// let p = outdir.display().to_string();
|
||||
|
||||
app.at("/")
|
||||
.get(|req: tide::Request<ServerState>| async move {
|
||||
log::info!("Connected to development server");
|
||||
let state = req.state();
|
||||
// app.at("/")
|
||||
// .get(|req: tide::Request<ServerState>| async move {
|
||||
// log::info!("Connected to development server");
|
||||
// let state = req.state();
|
||||
|
||||
match state.is_err.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
true => {
|
||||
//
|
||||
let mut resp =
|
||||
tide::Body::from_string(format!(include_str!("../err.html"), err = "_"));
|
||||
resp.set_mime(HTML);
|
||||
// match state.is_err.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
// true => {
|
||||
// //
|
||||
// let mut resp =
|
||||
// tide::Body::from_string(format!(include_str!("../err.html"), err = "_"));
|
||||
// resp.set_mime(HTML);
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
false => {
|
||||
Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?)
|
||||
}
|
||||
}
|
||||
})
|
||||
.serve_dir(p)?;
|
||||
// .serve_file(file_path)
|
||||
// .unwrap();
|
||||
// Ok(resp)
|
||||
// }
|
||||
// false => {
|
||||
// Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .serve_dir(p)?;
|
||||
// // .serve_file(file_path)
|
||||
// // .unwrap();
|
||||
|
||||
let port = "8080";
|
||||
let serve_addr = format!("127.0.0.1:{}", port);
|
||||
// let port = "8080";
|
||||
// let serve_addr = format!("127.0.0.1:{}", port);
|
||||
|
||||
info!("App available at http://{}/", serve_addr);
|
||||
app.listen(serve_addr).await?;
|
||||
Ok(())
|
||||
}
|
||||
// info!("App available at http://{}/", serve_addr);
|
||||
// app.listen(serve_addr).await?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// https://github.com/http-rs/tide/blob/main/examples/state.rs
|
||||
/// Tide seems to prefer using state instead of injecting into the app closure
|
||||
/// The app closure needs to be static and
|
||||
#[derive(Clone)]
|
||||
struct ServerState {
|
||||
serv_path: PathBuf,
|
||||
is_err: ErrStatus,
|
||||
}
|
||||
// /// https://github.com/http-rs/tide/blob/main/examples/state.rs
|
||||
// /// Tide seems to prefer using state instead of injecting into the app closure
|
||||
// /// The app closure needs to be static and
|
||||
// #[derive(Clone)]
|
||||
// struct ServerState {
|
||||
// serv_path: PathBuf,
|
||||
// is_err: ErrStatus,
|
||||
// }
|
||||
|
||||
type ErrStatus = Arc<AtomicBool>;
|
||||
// type ErrStatus = Arc<AtomicBool>;
|
||||
|
||||
impl ServerState {
|
||||
fn new(serv_path: PathBuf, is_err: ErrStatus) -> Self {
|
||||
Self { serv_path, is_err }
|
||||
}
|
||||
}
|
||||
// impl ServerState {
|
||||
// fn new(serv_path: PathBuf, is_err: ErrStatus) -> Self {
|
||||
// Self { serv_path, is_err }
|
||||
// }
|
||||
// }
|
||||
+30
-3
@@ -1,9 +1,36 @@
|
||||
use crate::cfg::ConfigOptsBuild;
|
||||
use crate::{cfg::ConfigOptsServe, server};
|
||||
use anyhow::Result;
|
||||
use std::path::PathBuf;
|
||||
use std::io::Write;
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// Build the Rust WASM app and all of its assets.
|
||||
mod develop;
|
||||
|
||||
/// Run the WASM project on dev-server
|
||||
#[derive(Clone, Debug, StructOpt)]
|
||||
#[structopt(name = "serve")]
|
||||
pub struct Serve {}
|
||||
pub struct Serve {
|
||||
#[structopt(flatten)]
|
||||
pub serve: ConfigOptsServe,
|
||||
}
|
||||
|
||||
impl Serve {
|
||||
pub async fn serve(self) -> anyhow::Result<()> {
|
||||
|
||||
let mut crate_config = crate::CrateConfig::new()?;
|
||||
// change the relase state.
|
||||
crate_config.with_release(self.serve.release);
|
||||
|
||||
crate::builder::build(&crate_config).expect("build failed");
|
||||
|
||||
let serve_html = String::from(include_str!("../../server/serve.html"));
|
||||
|
||||
let mut file = std::fs::File::create(crate_config.out_dir.join("index.html"))?;
|
||||
file.write_all(serve_html.as_bytes())?;
|
||||
|
||||
// start the develop server
|
||||
server::startup(crate_config.clone()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user