feat: add Dioxus.toml

This commit is contained in:
mrxiaozhuox committed 2022-01-23 17:35:59 +08:00
1 parent 70adff7e44
commit 9bb10c65a3
13 files changed
+267 -168

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
(function () {
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
var url = protocol + "//" + window.location.host + "/_dioxus/ws";
var poll_interval = 2000;
var reload_upon_connect = () => {
window.setTimeout(() => {
var ws = new WebSocket(url);
ws.onopen = () => window.location.reload();
ws.onclose = reload_upon_connect;
}, poll_interval);
};
var ws = new WebSocket(url);
ws.onmessage = (ev) => {
if (ev.data == "reload") {
window.location.reload();
}
};
ws.onclose = reload_upon_connect;
})();
+18
View File
@@ -0,0 +1,18 @@
<html>
<head>
<title>{app_title}</title>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<meta charset="UTF-8" />
{style_include}
</head>
<body>
<div id="main"></div>
<!-- Note the usage of `type=module` here as this is an ES6 module -->
<script type="module">
import init from "./assets/{app_name}.js";
init("./assets/{app_name}_bg.wasm");
</script>
{script_include}
</body>
</html>
+52 -27
View File
@@ -1,11 +1,12 @@
use crate::{ use crate::{
config::{CrateConfig, ExecutableType}, config::{CrateConfig, ExecutableType},
error::{Error, Result}, error::{Error, Result},
DioxusConfig,
}; };
use std::{io::Write, path::PathBuf, process::Command}; use std::process::Command;
use wasm_bindgen_cli_support::Bindgen; use wasm_bindgen_cli_support::Bindgen;
pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { pub fn build(config: &CrateConfig) -> Result<()> {
/* /*
[1] Build the project with cargo, generating a wasm32-unknown-unknown target (is there a more specific, better target to leverage?) [1] Build the project with cargo, generating a wasm32-unknown-unknown target (is there a more specific, better target to leverage?)
[2] Generate the appropriate build folders [2] Generate the appropriate build folders
@@ -15,15 +16,15 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> {
*/ */
let CrateConfig { let CrateConfig {
out_dir,
crate_dir, crate_dir,
target_dir, target_dir,
static_dir, static_dir,
executable, executable,
dioxus_config,
.. ..
} = config; } = config;
let out_dir = crate_dir.join(dist);
let t_start = std::time::Instant::now(); let t_start = std::time::Instant::now();
// [1] Build the .wasm module // [1] Build the .wasm module
@@ -83,7 +84,7 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> {
.keep_debug(true) .keep_debug(true)
.remove_name_section(false) .remove_name_section(false)
.remove_producers_section(false) .remove_producers_section(false)
.out_name("module") .out_name(&dioxus_config.application.name)
.generate(&bindgen_outdir)?; .generate(&bindgen_outdir)?;
let copy_options = fs_extra::dir::CopyOptions::new(); let copy_options = fs_extra::dir::CopyOptions::new();
@@ -101,28 +102,52 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> {
Ok(()) Ok(())
} }
pub fn gen_page(module: &str) -> String { pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
format!( let mut html = String::from(include_str!("./assets/index.html"));
r#"
<html> let resouces = config.web.resource.clone();
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" /> let mut style_list = resouces.style.unwrap_or(vec![]);
<meta charset="UTF-8" /> let mut script_list = resouces.script.unwrap_or(vec![]);
</head>
<body> if serve {
<div id="main"> let mut dev_style = resouces.dev.style.clone().unwrap_or(vec![]);
</div> let mut dev_script = resouces.dev.script.clone().unwrap_or(vec![]);
<!-- Note the usage of `type=module` here as this is an ES6 module --> style_list.append(&mut dev_style);
<script type="module"> script_list.append(&mut dev_script);
import init from "{}"; }
init("./assets/module_bg.wasm");
</script> let mut style_str = String::new();
<div id="dioxusroot"> </div> for style in style_list {
</body> style_str.push_str(&format!(
</html> "<link rel=\"stylesheet\" href=\"{}\">\n",
"#, &style.to_str().unwrap(),
module ))
) }
html = html.replace("{style_include}", &style_str);
let mut script_str = String::new();
for script in script_list {
script_str.push_str(&format!(
"<script src=\"{}\"></script>\n",
&script.to_str().unwrap(),
))
}
html = html.replace("{script_include}", &script_str);
if serve {
html += &format!(
"<script>{}</script>",
include_str!("./assets/autoreload.js")
);
}
html = html.replace("{app_name}", &config.application.name);
let title = config.web.app.title.clone().unwrap_or("dioxus | ⛺".into());
html.replace("{app_title}", &title)
} }
// use binary_install::{Cache, Download}; // use binary_install::{Cache, Download};
+1 -6
View File
@@ -1,5 +1,3 @@
use std::path::PathBuf;
use crate::cfg::ConfigOptsBuild; use crate::cfg::ConfigOptsBuild;
use structopt::StructOpt; use structopt::StructOpt;
@@ -18,10 +16,7 @@ impl Build {
// change the relase state. // change the relase state.
crate_config.with_release(self.build.release); crate_config.with_release(self.build.release);
crate::builder::build( crate::builder::build(&crate_config)?;
&crate_config,
self.build.dist.unwrap_or(PathBuf::from("dist")),
)?;
Ok(()) Ok(())
} }
-16
View File
@@ -16,14 +16,6 @@ pub struct ConfigOptsBuild {
#[serde(default)] #[serde(default)]
pub release: bool, 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>,
/// Optional pattern for the app loader script [default: None] /// Optional pattern for the app loader script [default: None]
/// ///
/// Patterns should include the sequences `{base}`, `{wasm}`, and `{js}` in order to /// Patterns should include the sequences `{base}`, `{wasm}`, and `{js}` in order to
@@ -74,14 +66,6 @@ pub struct ConfigOptsServe {
#[structopt(long)] #[structopt(long)]
#[serde(default)] #[serde(default)]
pub release: bool, 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. /// Ensure the given value for `--public-url` is formatted correctly.
+19 -8
View File
@@ -13,9 +13,9 @@ use crate::error::{Error, Result};
#[derive(Clone, Debug, StructOpt)] #[derive(Clone, Debug, StructOpt)]
#[structopt(name = "init")] #[structopt(name = "init")]
pub struct Init { pub struct Init {
/// Init project path /// Init project name
#[structopt(default_value = ".")] #[structopt(default_value = ".")]
path: String, name: String,
/// Template path /// Template path
#[structopt(default_value = "default", long)] #[structopt(default_value = "default", long)]
@@ -24,18 +24,23 @@ pub struct Init {
impl Init { impl Init {
pub fn init(self) -> Result<()> { pub fn init(self) -> Result<()> {
log::info!("🔧 Start to init a new project '{}'.", self.path); if self.name.contains(".") {
log::error!("❗Unsupported project name.");
return Ok(());
}
let project_path = PathBuf::from(&self.path); log::info!("🔧 Start to init a new project '{}'.", self.name);
let project_path = PathBuf::from(&self.name);
if project_path.join("Cargo.toml").is_file() { if project_path.join("Cargo.toml").is_file() {
log::warn!("Path '{}' is initialized.", self.path); log::warn!("Folder '{}' is initialized.", self.name);
return Ok(()); return Ok(());
} }
let output = Command::new("cargo") let output = Command::new("cargo")
.arg("init") .arg("init")
.arg(&self.path) .arg(&format!("./{}", self.name))
.arg("--bin") .arg("--bin")
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
@@ -58,6 +63,11 @@ impl Init {
let mut file = File::create(main_rs_file)?; let mut file = File::create(main_rs_file)?;
file.write_all(&template_str.as_bytes())?; file.write_all(&template_str.as_bytes())?;
let mut file = File::create(project_path.join("Dioxus.toml"))?;
let dioxus_conf = String::from(include_str!("../../template/config.toml"))
.replace("{project-name}", &self.name);
file.write_all(dioxus_conf.as_bytes())?;
// log::info!("🎯 Project initialization completed."); // log::info!("🎯 Project initialization completed.");
if !Command::new("cargo") if !Command::new("cargo")
@@ -72,8 +82,9 @@ impl Init {
file.write_all("dioxus = { version = \"0.1.7\", features = [\"web\"] }".as_bytes())?; file.write_all("dioxus = { version = \"0.1.7\", features = [\"web\"] }".as_bytes())?;
} }
log::info!("\n💡 Project initialized:"); println!("");
log::info!("🎯> cd {}", self.path); log::info!("💡 Project initialized:");
log::info!("🎯> cd ./{}", self.name);
log::info!("🎯> dioxus serve"); log::info!("🎯> dioxus serve");
Ok(()) Ok(())
+20 -8
View File
@@ -1,4 +1,4 @@
use crate::{cfg::ConfigOptsServe, server}; use crate::{cfg::ConfigOptsServe, gen_page, server};
use std::{io::Write, path::PathBuf}; use std::{io::Write, path::PathBuf};
use structopt::StructOpt; use structopt::StructOpt;
@@ -16,19 +16,31 @@ impl Serve {
// change the relase state. // change the relase state.
crate_config.with_release(self.serve.release); crate_config.with_release(self.serve.release);
let dist_path = self.serve.dist.clone().unwrap_or(PathBuf::from("dist")); crate::builder::build(&crate_config).expect("build failed");
crate::builder::build(&crate_config, dist_path.clone()).expect("build failed"); let serve_html = gen_page(&crate_config.dioxus_config, true);
let serve_html = String::from(include_str!("../../server/serve.html")); let mut file = std::fs::File::create(
crate_config
let mut file = .crate_dir
std::fs::File::create(crate_config.crate_dir.join(dist_path).join("index.html"))?; .join(
crate_config
.dioxus_config
.web
.app
.out_dir
.clone()
.unwrap_or(PathBuf::from("dist")),
)
.join("index.html"),
)?;
file.write_all(serve_html.as_bytes())?; file.write_all(serve_html.as_bytes())?;
// start the develop server // start the develop server
server::startup(crate_config.clone(), &self.serve).await?; server::startup(crate_config.clone()).await?;
Ok(()) Ok(())
} }
pub fn regen_page() {}
} }
+61 -15
View File
@@ -1,43 +1,76 @@
use crate::error::Result; use crate::error::Result;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::{fs::File, io::Read, path::PathBuf};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DioxusConfig { pub struct DioxusConfig {
application: ApplicationConfig, pub application: ApplicationConfig,
web: WebConfig, pub web: WebConfig,
}
impl DioxusConfig {
pub fn load() -> crate::error::Result<DioxusConfig> {
let crate_dir = crate::cargo::crate_root()?;
if !crate_dir.join("Dioxus.toml").is_file() {
return Err(crate::error::Error::CargoError(
"Dioxus.toml not found".into(),
));
}
let mut dioxus_conf_file = File::open(crate_dir.join("Dioxus.toml"))?;
let mut meta_str = String::new();
dioxus_conf_file.read_to_string(&mut meta_str)?;
match toml::from_str::<DioxusConfig>(&meta_str) {
Ok(v) => return Ok(v),
Err(e) => {
log::error!("{}", e);
return Err(crate::error::Error::Unique(
"Dioxus.toml parse failed".into(),
));
}
}
}
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApplicationConfig { pub struct ApplicationConfig {
name: String, pub name: String,
platform: Vec<String>, pub platforms: Vec<String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebConfig { pub struct WebConfig {
app: WebAppConfing, pub app: WebAppConfing,
watcher: WebWatcherConfing, pub watcher: WebWatcherConfing,
resource: WebResourceConfing, pub resource: WebResourceConfing,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebAppConfing { pub struct WebAppConfing {
pub title: Option<String>,
pub out_dir: Option<PathBuf>,
pub static_dir: Option<PathBuf>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebWatcherConfing { pub struct WebWatcherConfing {
pub watch_path: Option<Vec<PathBuf>>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebResourceConfing { pub struct WebResourceConfing {
pub dev: WebDevResourceConfing,
pub style: Option<Vec<PathBuf>>,
pub script: Option<Vec<PathBuf>>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebDevResourceConfing {
pub style: Option<Vec<PathBuf>>,
pub script: Option<Vec<PathBuf>>,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CrateConfig { pub struct CrateConfig {
@@ -48,6 +81,7 @@ pub struct CrateConfig {
pub static_dir: PathBuf, pub static_dir: PathBuf,
pub manifest: cargo_toml::Manifest<cargo_toml::Value>, pub manifest: cargo_toml::Manifest<cargo_toml::Value>,
pub executable: ExecutableType, pub executable: ExecutableType,
pub dioxus_config: DioxusConfig,
pub release: bool, pub release: bool,
} }
@@ -60,12 +94,23 @@ pub enum ExecutableType {
impl CrateConfig { impl CrateConfig {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let dioxus_config = DioxusConfig::load()?;
let crate_dir = crate::cargo::crate_root()?; let crate_dir = crate::cargo::crate_root()?;
let workspace_dir = crate::cargo::workspace_root()?; let workspace_dir = crate::cargo::workspace_root()?;
let target_dir = workspace_dir.join("target"); let target_dir = workspace_dir.join("target");
let out_dir = crate_dir.join("public");
let out_dir = match dioxus_config.web.app.out_dir {
Some(ref v) => crate_dir.join(v),
None => crate_dir.join("dist"),
};
let cargo_def = &crate_dir.join("Cargo.toml"); let cargo_def = &crate_dir.join("Cargo.toml");
let static_dir = crate_dir.join("static");
let static_dir = match dioxus_config.web.app.static_dir {
Some(ref v) => crate_dir.join(v),
None => crate_dir.join("static"),
};
let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap(); let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap();
@@ -96,6 +141,7 @@ impl CrateConfig {
manifest, manifest,
executable, executable,
release, release,
dioxus_config,
}) })
} }
-19
View File
@@ -1,19 +0,0 @@
<html>
<head></head>
<body>
<div>
<h1>
Sorry, but building your application failed.
</h1>
<p>
Here's the error:
</p>
<code>
{err}
</code>
</div>
</body>
</html>
+33 -21
View File
@@ -13,7 +13,7 @@ use std::{
}; };
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use crate::{builder, CrateConfig}; use crate::{builder, crate_root, CrateConfig};
struct WsRelodState { struct WsRelodState {
update: bool, update: bool,
@@ -25,42 +25,54 @@ impl WsRelodState {
} }
} }
pub async fn startup( pub async fn startup(config: CrateConfig) -> anyhow::Result<()> {
config: CrateConfig,
opts: &crate::cfg::ConfigOptsServe,
) -> anyhow::Result<()> {
log::info!("🚀 Starting development server..."); log::info!("🚀 Starting development server...");
let (tx, rx) = channel(); let (tx, rx) = channel();
let dist_path = opts.dist.clone().unwrap_or(PathBuf::from("dist")); let dist_path = config.out_dir.clone();
// file watcher: check file change // file watcher: check file change
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
watcher watcher
.watch( .watch(config.crate_dir.clone(), notify::RecursiveMode::Recursive)
config.crate_dir.join("src").clone(),
notify::RecursiveMode::Recursive,
)
.unwrap(); .unwrap();
let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false })); let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false }));
let watcher_conf = config.clone(); let watcher_conf = config.clone();
let watcher_ws_state = ws_reload_state.clone(); let watcher_ws_state = ws_reload_state.clone();
let watcher_dist_path = dist_path.clone();
tokio::spawn(async move { tokio::spawn(async move {
let allow_watch_path = watcher_conf
.dioxus_config
.web
.watcher
.watch_path
.clone()
.unwrap_or(vec![PathBuf::from("src")]);
let crate_dir = watcher_conf.crate_dir.clone();
loop { loop {
if let Ok(v) = rx.recv() { if let Ok(v) = rx.recv() {
match v { match v {
DebouncedEvent::Create(_) DebouncedEvent::Create(e)
| DebouncedEvent::Write(_) | DebouncedEvent::Write(e)
| DebouncedEvent::Remove(_) | DebouncedEvent::Remove(e)
| DebouncedEvent::Rename(_, _) => { | DebouncedEvent::Rename(e, _) => {
if let Ok(_) = builder::build(&watcher_conf, watcher_dist_path.clone()) { let mut reload = false;
// change the websocket reload state to true; for path in &allow_watch_path {
// the page will auto-reload. let temp = crate_dir.clone().join(path);
watcher_ws_state.lock().unwrap().change(); if e.starts_with(temp) {
reload = true;
break;
}
}
if reload {
if let Ok(_) = builder::build(&watcher_conf) {
// change the websocket reload state to true;
// the page will auto-reload.
watcher_ws_state.lock().unwrap().change();
}
} }
} }
_ => {} _ => {}
@@ -70,7 +82,7 @@ pub async fn startup(
}); });
let app = Router::new() let app = Router::new()
.route("/ws", get(ws_handler)) .route("/_dioxus/ws", get(ws_handler))
.fallback( .fallback(
get_service(ServeDir::new(config.crate_dir.join(&dist_path))).handle_error( get_service(ServeDir::new(config.crate_dir.join(&dist_path))).handle_error(
|error: std::io::Error| async move { |error: std::io::Error| async move {
-39
View File
@@ -1,39 +0,0 @@
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<meta charset="UTF-8" />
<title>Dioxus-CLI Dev Server</title>
</head>
<body>
<div id="main"></div>
<!-- Note the usage of `type=module` here as this is an ES6 module -->
<script type="module">
import init from "./assets/module.js";
init("./assets/module_bg.wasm");
</script>
<script>
(function () {
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
var url = protocol + "//" + window.location.host + "/ws";
var poll_interval = 2000;
var reload_upon_connect = () => {
window.setTimeout(() => {
var ws = new WebSocket(url);
ws.onopen = () => window.location.reload();
ws.onclose = reload_upon_connect;
}, poll_interval);
};
var ws = new WebSocket(url);
ws.onmessage = (ev) => {
if (ev.data == "reload") {
window.location.reload();
}
};
ws.onclose = reload_upon_connect;
})();
</script>
</body>
</html>
+36 -7
View File
@@ -1,9 +1,38 @@
[package] [application]
name = "{project}"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # App (Project) Name
name = "{project-name}"
[dependencies] # Dioxus App Support Platform
dioxus = { version = "0.1.7", features = ["web"] } # desktop, web, mobile, ssr
platforms = ["web"]
[web.app]
# HTML title tag content
title = "dioxus | ⛺"
# Web `build` & `serve` dist path
out_dir = "dist"
# resource (static) file folder
static_dir = "static"
[web.watcher]
watch_path = ["src"]
# include `assets` in web platform
[web.resource]
# CSS style file
style = []
# Javascript code file
script = []
[web.resource.dev]
# Javascript code file
# serve: [dev-server] only
script = []
+2 -2
View File
@@ -8,8 +8,8 @@ fn app(cx: Scope) -> Element {
cx.render(rsx! ( cx.render(rsx! (
div { div {
style: "text-align: center;", style: "text-align: center;",
h2 { "🌗🚀 Dioxus" } h1 { "🌗 Dioxus 🚀" }
p { strong { "Frontend that scales." } } h3 { "Frontend that scales." }
p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." } p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." }
} }
)) ))