15 files changed
+5091
-81
No files matched your search
@@ -1,3 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
|
||||||
.idea/
|
.idea/
|
||||||
Generated
+4732
File diff suppressed because it is too large.
Load diff
+4
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dioxus-cli"
|
name = "dioxus-cli"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
authors = ["Jonathan Kelley"]
|
authors = ["Jonathan Kelley"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "CLI tool for developing, testing, and publishing Dioxus apps"
|
description = "CLI tool for developing, testing, and publishing Dioxus apps"
|
||||||
@@ -14,6 +14,7 @@ license = "MIT/Apache-2.0"
|
|||||||
clap = { version = "3.0.14", features = ["derive"] }
|
clap = { version = "3.0.14", features = ["derive"] }
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
wasm-bindgen-cli-support = "0.2.79"
|
wasm-bindgen-cli-support = "0.2.79"
|
||||||
|
colored = "2.0.0"
|
||||||
|
|
||||||
# features
|
# features
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
@@ -28,13 +29,14 @@ notify = { version = "5.0.0-pre.13", features = ["serde"] }
|
|||||||
html_parser = "0.6.2"
|
html_parser = "0.6.2"
|
||||||
binary-install = "0.0.2"
|
binary-install = "0.0.2"
|
||||||
convert_case = "0.5.0"
|
convert_case = "0.5.0"
|
||||||
cargo_metadata = "0.14.1"
|
cargo_metadata = "0.15.0"
|
||||||
tokio = { version = "1.16.1", features = ["full"] }
|
tokio = { version = "1.16.1", features = ["full"] }
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
regex = "1.5.4"
|
regex = "1.5.4"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
anyhow = "1.0.53"
|
anyhow = "1.0.53"
|
||||||
hyper = "0.14.17"
|
hyper = "0.14.17"
|
||||||
|
indicatif = "0.17.0-rc.11"
|
||||||
|
|
||||||
axum = { version = "0.5.1", features = ["ws", "headers"] }
|
axum = { version = "0.5.1", features = ["ws", "headers"] }
|
||||||
tower-http = { version = "0.2.2", features = ["fs", "trace"] }
|
tower-http = { version = "0.2.2", features = ["fs", "trace"] }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>📦✨ dioxus-cli </h1>
|
<h1>📦✨ Dioxus CLI </h1>
|
||||||
<p><strong>Tooling to supercharge dioxus projects</strong></p>
|
<p><strong>Tooling to supercharge dioxus projects</strong></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ asset_dir = "public"
|
|||||||
[web.app]
|
[web.app]
|
||||||
|
|
||||||
# HTML title tag content
|
# HTML title tag content
|
||||||
title = "dioxus | ⛺"
|
title = "Dioxus | An elegant GUI library for Rust"
|
||||||
|
|
||||||
|
index_on_404 = true
|
||||||
|
|
||||||
[web.watcher]
|
[web.watcher]
|
||||||
|
|
||||||
@@ -45,10 +47,12 @@ script = []
|
|||||||
binaryen = { wasm_opt = true }
|
binaryen = { wasm_opt = true }
|
||||||
|
|
||||||
# use sass auto will auto check sass file and build it.
|
# use sass auto will auto check sass file and build it.
|
||||||
[application.tools.sass]
|
|
||||||
|
|
||||||
|
# [application.tools.sass]
|
||||||
|
|
||||||
# auto will check the assets dirs, and auto to transform all scss file to css file.
|
# auto will check the assets dirs, and auto to transform all scss file to css file.
|
||||||
input = "*"
|
# input = "*"
|
||||||
|
|
||||||
# or you can specify some scss file -> css file
|
# or you can specify some scss file -> css file
|
||||||
# input = [
|
# input = [
|
||||||
@@ -57,4 +61,4 @@ input = "*"
|
|||||||
# "/css/test.scss"
|
# "/css/test.scss"
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
source_map = true
|
# source_map = true
|
||||||
@@ -9,10 +9,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main"></div>
|
<div id="main"></div>
|
||||||
<!-- Note the usage of `type=module` here as this is an ES6 module -->
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import init from "{base_path}/assets/dioxus/{app_name}.js";
|
import init from "/{base_path}/assets/dioxus/{app_name}.js";
|
||||||
init("{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {
|
init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {
|
||||||
if (wasm.__wbindgen_start == undefined) {
|
if (wasm.__wbindgen_start == undefined) {
|
||||||
wasm.main();
|
wasm.main();
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-15
@@ -4,16 +4,26 @@ use crate::{
|
|||||||
tools::Tool,
|
tools::Tool,
|
||||||
DioxusConfig,
|
DioxusConfig,
|
||||||
};
|
};
|
||||||
|
use cargo_metadata::{diagnostic::Diagnostic, Message};
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
use serde::Serialize;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{copy, create_dir_all, remove_dir_all, File},
|
fs::{copy, create_dir_all, remove_dir_all, File},
|
||||||
io::Read,
|
io::Read,
|
||||||
panic,
|
panic,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Command,
|
process::Command,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
use wasm_bindgen_cli_support::Bindgen;
|
use wasm_bindgen_cli_support::Bindgen;
|
||||||
|
|
||||||
pub fn build(config: &CrateConfig) -> Result<()> {
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
pub struct BuildResult {
|
||||||
|
pub warnings: Vec<Diagnostic>,
|
||||||
|
pub elapsed_time: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
|
||||||
// [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
|
||||||
// [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm
|
// [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm
|
||||||
@@ -42,8 +52,9 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||||||
.arg("build")
|
.arg("build")
|
||||||
.arg("--target")
|
.arg("--target")
|
||||||
.arg("wasm32-unknown-unknown")
|
.arg("wasm32-unknown-unknown")
|
||||||
.stdout(std::process::Stdio::inherit())
|
.arg("--message-format=json")
|
||||||
.stderr(std::process::Stdio::inherit());
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped());
|
||||||
|
|
||||||
if config.release {
|
if config.release {
|
||||||
cmd.arg("--release");
|
cmd.arg("--release");
|
||||||
@@ -52,6 +63,10 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||||||
cmd.arg("--verbose");
|
cmd.arg("--verbose");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if quiet {
|
||||||
|
cmd.arg("--quiet");
|
||||||
|
}
|
||||||
|
|
||||||
if config.custom_profile.is_some() {
|
if config.custom_profile.is_some() {
|
||||||
let custom_profile = config.custom_profile.as_ref().unwrap();
|
let custom_profile = config.custom_profile.as_ref().unwrap();
|
||||||
cmd.arg("--profile");
|
cmd.arg("--profile");
|
||||||
@@ -70,13 +85,7 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||||||
ExecutableType::Example(name) => cmd.arg("--example").arg(name),
|
ExecutableType::Example(name) => cmd.arg("--example").arg(name),
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = cmd.output()?;
|
let warning_messages = prettier_build(cmd)?;
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
log::error!("Build failed!");
|
|
||||||
let reason = String::from_utf8_lossy(&output.stderr).to_string();
|
|
||||||
return Err(Error::BuildFailed(reason));
|
|
||||||
}
|
|
||||||
|
|
||||||
// [2] Establish the output directory structure
|
// [2] Establish the output directory structure
|
||||||
let bindgen_outdir = out_dir.join("assets").join("dioxus");
|
let bindgen_outdir = out_dir.join("assets").join("dioxus");
|
||||||
@@ -114,8 +123,7 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
if bindgen_result.is_err() {
|
if bindgen_result.is_err() {
|
||||||
log::error!("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.79` Bindgen crate.");
|
return Err(Error::BuildFailed("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.79` Bindgen crate.".to_string()));
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check binaryen:wasm-opt tool
|
// check binaryen:wasm-opt tool
|
||||||
@@ -185,8 +193,10 @@ pub fn build(config: &CrateConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let t_end = std::time::Instant::now();
|
let t_end = std::time::Instant::now();
|
||||||
log::info!("🏁 Done in {}ms!", (t_end - t_start).as_millis());
|
Ok(BuildResult {
|
||||||
Ok(())
|
warnings: warning_messages,
|
||||||
|
elapsed_time: (t_end - t_start).as_millis(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
|
pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
|
||||||
@@ -319,6 +329,57 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prettier_build(mut cmd: Command) -> anyhow::Result<Vec<Diagnostic>> {
|
||||||
|
let mut warning_messages: Vec<Diagnostic> = vec![];
|
||||||
|
|
||||||
|
let pb = ProgressBar::new_spinner();
|
||||||
|
pb.enable_steady_tick(Duration::from_millis(200));
|
||||||
|
pb.set_style(
|
||||||
|
ProgressStyle::with_template("{spinner:.dim.bold} {wide_msg}")
|
||||||
|
.unwrap()
|
||||||
|
.tick_chars("/|\\- "),
|
||||||
|
);
|
||||||
|
pb.set_message("💼 Waiting to start build the project...");
|
||||||
|
|
||||||
|
let mut command = cmd.spawn()?;
|
||||||
|
let reader = std::io::BufReader::new(command.stdout.take().unwrap());
|
||||||
|
for message in cargo_metadata::Message::parse_stream(reader) {
|
||||||
|
match message.unwrap() {
|
||||||
|
Message::CompilerMessage(msg) => {
|
||||||
|
let message = msg.message;
|
||||||
|
match message.level {
|
||||||
|
cargo_metadata::diagnostic::DiagnosticLevel::Error => {
|
||||||
|
return Err(anyhow::anyhow!(message
|
||||||
|
.rendered
|
||||||
|
.unwrap_or("Unknown".into())));
|
||||||
|
}
|
||||||
|
cargo_metadata::diagnostic::DiagnosticLevel::Warning => {
|
||||||
|
warning_messages.push(message.clone());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::CompilerArtifact(artifact) => {
|
||||||
|
pb.set_message(format!("Compiling {} ", artifact.package_id));
|
||||||
|
pb.tick();
|
||||||
|
}
|
||||||
|
Message::BuildScriptExecuted(script) => {
|
||||||
|
let _package_id = script.package_id.to_string();
|
||||||
|
}
|
||||||
|
Message::BuildFinished(finished) => {
|
||||||
|
if finished.success {
|
||||||
|
pb.finish_and_clear();
|
||||||
|
log::info!("👑 Build done.");
|
||||||
|
} else {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (), // Unknown message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(warning_messages)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
|
pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
|
||||||
let crate_root = crate::cargo::crate_root().unwrap();
|
let crate_root = crate::cargo::crate_root().unwrap();
|
||||||
let custom_html_file = crate_root.join("index.html");
|
let custom_html_file = crate_root.join("index.html");
|
||||||
@@ -429,7 +490,9 @@ fn build_assets(config: &CrateConfig) -> Result<Vec<PathBuf>> {
|
|||||||
let temp = entry.path();
|
let temp = entry.path();
|
||||||
if temp.is_file() {
|
if temp.is_file() {
|
||||||
let suffix = temp.extension();
|
let suffix = temp.extension();
|
||||||
if suffix.is_none() { continue; }
|
if suffix.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let suffix = suffix.unwrap().to_str().unwrap();
|
let suffix = suffix.unwrap().to_str().unwrap();
|
||||||
if suffix == "scss" || suffix == "sass" {
|
if suffix == "scss" || suffix == "sass" {
|
||||||
// if file suffix is `scss` / `sass` we need transform it.
|
// if file suffix is `scss` / `sass` we need transform it.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ impl Build {
|
|||||||
|
|
||||||
match platform.as_str() {
|
match platform.as_str() {
|
||||||
"web" => {
|
"web" => {
|
||||||
crate::builder::build(&crate_config)?;
|
crate::builder::build(&crate_config, false)?;
|
||||||
}
|
}
|
||||||
"desktop" => {
|
"desktop" => {
|
||||||
crate::builder::build_desktop(&crate_config, false)?;
|
crate::builder::build_desktop(&crate_config, false)?;
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ impl Create {
|
|||||||
let mut meta_file = String::new();
|
let mut meta_file = String::new();
|
||||||
dioxus_file.read_to_string(&mut meta_file)?;
|
dioxus_file.read_to_string(&mut meta_file)?;
|
||||||
meta_file = meta_file.replace("{{project-name}}", &self.name);
|
meta_file = meta_file.replace("{{project-name}}", &self.name);
|
||||||
|
meta_file = meta_file.replace("{{default-platform}}", "web");
|
||||||
File::create(project_path.join("Dioxus.toml"))?.write_all(meta_file.as_bytes())?;
|
File::create(project_path.join("Dioxus.toml"))?.write_all(meta_file.as_bytes())?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|||||||
+1
-5
@@ -27,7 +27,7 @@ use std::{
|
|||||||
|
|
||||||
/// Build, bundle, & ship your Dioxus app.
|
/// Build, bundle, & ship your Dioxus app.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(name = "dioxus")]
|
#[clap(name = "dioxus", version)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub action: Commands,
|
pub action: Commands,
|
||||||
@@ -35,10 +35,6 @@ pub struct Cli {
|
|||||||
/// Enable verbose logging.
|
/// Enable verbose logging.
|
||||||
#[clap(short)]
|
#[clap(short)]
|
||||||
pub v: bool,
|
pub v: bool,
|
||||||
// // note: dioxus is still roughly compatible with trunk
|
|
||||||
// /// Path to the Trunk config file [default: Trunk.toml]
|
|
||||||
// #[clap(long, parse(from_os_str), env = "TRUNK_CONFIG")]
|
|
||||||
// pub config: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|||||||
+15
-21
@@ -38,31 +38,25 @@ impl Serve {
|
|||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
match platform.as_str() {
|
if platform.as_str() == "desktop" {
|
||||||
"web" => {
|
crate::builder::build_desktop(&crate_config, true)?;
|
||||||
crate::builder::build(&crate_config)?;
|
|
||||||
}
|
|
||||||
"desktop" => {
|
|
||||||
crate::builder::build_desktop(&crate_config, true)?;
|
|
||||||
|
|
||||||
match &crate_config.executable {
|
match &crate_config.executable {
|
||||||
crate::ExecutableType::Binary(name)
|
crate::ExecutableType::Binary(name)
|
||||||
| crate::ExecutableType::Lib(name)
|
| crate::ExecutableType::Lib(name)
|
||||||
| crate::ExecutableType::Example(name) => {
|
| crate::ExecutableType::Example(name) => {
|
||||||
let mut file = crate_config.out_dir.join(name);
|
let mut file = crate_config.out_dir.join(name);
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
file.set_extension("exe");
|
file.set_extension("exe");
|
||||||
}
|
|
||||||
Command::new(crate_config.out_dir.join(file).to_str().unwrap())
|
|
||||||
.stdout(Stdio::inherit())
|
|
||||||
.output()?;
|
|
||||||
}
|
}
|
||||||
|
Command::new(crate_config.out_dir.join(file).to_str().unwrap())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.output()?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return custom_error!("Unsoppurt platform target.");
|
|
||||||
}
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if platform != "web" {
|
||||||
|
return custom_error!("Unsoppurt platform target.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate dev-index page
|
// generate dev-index page
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pub const DIOXUS_CLI_VERSION: &'static str = "0.1.5";
|
||||||
|
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
|
|||||||
+5
-5
@@ -9,31 +9,31 @@ async fn main() -> Result<()> {
|
|||||||
match args.action {
|
match args.action {
|
||||||
Commands::Translate(opts) => {
|
Commands::Translate(opts) => {
|
||||||
if let Err(e) = opts.translate() {
|
if let Err(e) = opts.translate() {
|
||||||
log::error!("translate error: {}", e);
|
log::error!("🚫 Translate failed: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Build(opts) => {
|
Commands::Build(opts) => {
|
||||||
if let Err(e) = opts.build() {
|
if let Err(e) = opts.build() {
|
||||||
log::error!("build error: {}", e);
|
log::error!("🚫 Build project failed: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Clean(opts) => {
|
Commands::Clean(opts) => {
|
||||||
if let Err(e) = opts.clean() {
|
if let Err(e) = opts.clean() {
|
||||||
log::error!("clean error: {}", e);
|
log::error!("🚫 Clean project failed: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Serve(opts) => {
|
Commands::Serve(opts) => {
|
||||||
if let Err(e) = opts.serve().await {
|
if let Err(e) = opts.serve().await {
|
||||||
log::error!("serve error: {}", e);
|
log::error!("🚫 Serve startup failed: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Create(opts) => {
|
Commands::Create(opts) => {
|
||||||
if let Err(e) = opts.create() {
|
if let Err(e) = opts.create() {
|
||||||
log::error!("create error: {}", e);
|
log::error!("🚫 Create project failed: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub struct FileMap {
|
|||||||
|
|
||||||
impl FileMap {
|
impl FileMap {
|
||||||
pub fn new(path: PathBuf) -> Self {
|
pub fn new(path: PathBuf) -> Self {
|
||||||
log::info!("Searching files for changes since last compile...");
|
log::info!("🔮 Searching files for changes since last compile...");
|
||||||
fn find_rs_files(root: PathBuf) -> io::Result<HashMap<PathBuf, String>> {
|
fn find_rs_files(root: PathBuf) -> io::Result<HashMap<PathBuf, String>> {
|
||||||
let mut files = HashMap::new();
|
let mut files = HashMap::new();
|
||||||
if root.is_dir() {
|
if root.is_dir() {
|
||||||
@@ -61,7 +61,7 @@ impl FileMap {
|
|||||||
last_updated_time,
|
last_updated_time,
|
||||||
map: find_rs_files(path).unwrap(),
|
map: find_rs_files(path).unwrap(),
|
||||||
};
|
};
|
||||||
log::info!("Files updated");
|
// log::info!("Files updated");
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ pub async fn hot_reload_handler(
|
|||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
log::info!("Finding updates since last compile...");
|
log::info!("🔮 Finding updates since last compile...");
|
||||||
let handle = state.last_file_rebuild.lock().unwrap();
|
let handle = state.last_file_rebuild.lock().unwrap();
|
||||||
let update_time = handle.last_updated_time.clone();
|
let update_time = handle.last_updated_time.clone();
|
||||||
for (k, v) in handle.map.iter() {
|
for (k, v) in handle.map.iter() {
|
||||||
@@ -154,7 +154,7 @@ pub async fn hot_reload_handler(
|
|||||||
log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file_path, parse_error.location.line, parse_error.location.column, parse_error.message);
|
log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file_path, parse_error.location.line, parse_error.location.column, parse_error.message);
|
||||||
},
|
},
|
||||||
Error::RecompileRequiredError(_) => {
|
Error::RecompileRequiredError(_) => {
|
||||||
if let Err(err) = state.build_manager.build(){
|
if let Err(err) = state.build_manager.rebuild(){
|
||||||
log::error!("{}", err);
|
log::error!("{}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+237
-19
@@ -6,15 +6,17 @@ use axum::{
|
|||||||
routing::{get, get_service},
|
routing::{get, get_service},
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
use cargo_metadata::diagnostic::Diagnostic;
|
||||||
|
use colored::Colorize;
|
||||||
use dioxus::rsx_interpreter::SetRsxMessage;
|
use dioxus::rsx_interpreter::SetRsxMessage;
|
||||||
use notify::{RecommendedWatcher, Watcher};
|
use notify::{RecommendedWatcher, Watcher};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{net::UdpSocket, path::PathBuf, process::Command, sync::Arc};
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody};
|
use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody};
|
||||||
|
|
||||||
use crate::{builder, serve::Serve, CrateConfig, Result};
|
use crate::{builder, serve::Serve, BuildResult, CrateConfig, Result};
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
mod hot_reload;
|
mod hot_reload;
|
||||||
@@ -26,9 +28,9 @@ pub struct BuildManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BuildManager {
|
impl BuildManager {
|
||||||
fn build(&self) -> Result<()> {
|
fn rebuild(&self) -> Result<BuildResult> {
|
||||||
log::info!("Start to rebuild project...");
|
log::info!("🪁 Rebuild project");
|
||||||
builder::build(&self.config)?;
|
let result = builder::build(&self.config, true)?;
|
||||||
// change the websocket reload state to true;
|
// change the websocket reload state to true;
|
||||||
// the page will auto-reload.
|
// the page will auto-reload.
|
||||||
if self
|
if self
|
||||||
@@ -42,7 +44,7 @@ impl BuildManager {
|
|||||||
let _ = Serve::regen_dev_page(&self.config);
|
let _ = Serve::regen_dev_page(&self.config);
|
||||||
}
|
}
|
||||||
let _ = self.reload_tx.send(());
|
let _ = self.reload_tx.send(());
|
||||||
Ok(())
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,8 @@ pub async fn startup(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
|
let first_build_result = crate::builder::build(&config, false)?;
|
||||||
|
|
||||||
log::info!("🚀 Starting development server...");
|
log::info!("🚀 Starting development server...");
|
||||||
|
|
||||||
let dist_path = config.out_dir.clone();
|
let dist_path = config.out_dir.clone();
|
||||||
@@ -93,14 +97,16 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| vec![PathBuf::from("src")]);
|
.unwrap_or_else(|| vec![PathBuf::from("src")]);
|
||||||
|
|
||||||
|
let watcher_config = config.clone();
|
||||||
let mut watcher = RecommendedWatcher::new(move |evt: notify::Result<notify::Event>| {
|
let mut watcher = RecommendedWatcher::new(move |evt: notify::Result<notify::Event>| {
|
||||||
|
let config = watcher_config.clone();
|
||||||
if chrono::Local::now().timestamp() > last_update_time {
|
if chrono::Local::now().timestamp() > last_update_time {
|
||||||
// Give time for the change to take effect before reading the file
|
// Give time for the change to take effect before reading the file
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
if let Ok(evt) = evt {
|
if let Ok(evt) = evt {
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
let mut needs_rebuild = false;
|
let mut needs_rebuild = false;
|
||||||
for path in evt.paths {
|
for path in evt.paths.clone() {
|
||||||
let mut file = File::open(path.clone()).unwrap();
|
let mut file = File::open(path.clone()).unwrap();
|
||||||
if path.extension().map(|p| p.to_str()).flatten() != Some("rs") {
|
if path.extension().map(|p| p.to_str()).flatten() != Some("rs") {
|
||||||
continue;
|
continue;
|
||||||
@@ -118,7 +124,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
last_file_rebuild.map.insert(path, src);
|
last_file_rebuild.map.insert(path, src);
|
||||||
}
|
}
|
||||||
DiffResult::RsxChanged(changed) => {
|
DiffResult::RsxChanged(changed) => {
|
||||||
log::info!("reloading rsx");
|
log::info!("🪁 reloading rsx");
|
||||||
for (old, new) in changed.into_iter() {
|
for (old, new) in changed.into_iter() {
|
||||||
let hr = get_location(
|
let hr = get_location(
|
||||||
&crate_dir,
|
&crate_dir,
|
||||||
@@ -156,9 +162,21 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needs_rebuild {
|
if needs_rebuild {
|
||||||
log::info!("reload required");
|
match build_manager.rebuild() {
|
||||||
if let Err(err) = build_manager.build() {
|
Ok(res) => {
|
||||||
log::error!("{}", err);
|
print_console_info(
|
||||||
|
port,
|
||||||
|
&config,
|
||||||
|
PrettierOptions {
|
||||||
|
changed: evt.paths,
|
||||||
|
warnings: res.warnings,
|
||||||
|
elapsed_time: res.elapsed_time,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("{}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !messages.is_empty() {
|
if !messages.is_empty() {
|
||||||
@@ -180,7 +198,15 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start serve dev-server at 0.0.0.0:8080
|
// start serve dev-server at 0.0.0.0:8080
|
||||||
log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port);
|
print_console_info(
|
||||||
|
port,
|
||||||
|
&config,
|
||||||
|
PrettierOptions {
|
||||||
|
changed: vec![],
|
||||||
|
warnings: first_build_result.warnings,
|
||||||
|
elapsed_time: first_build_result.elapsed_time,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let file_service_config = config.clone();
|
let file_service_config = config.clone();
|
||||||
let file_service = ServiceBuilder::new()
|
let file_service = ServiceBuilder::new()
|
||||||
@@ -243,6 +269,8 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
|
let first_build_result = crate::builder::build(&config, false)?;
|
||||||
|
|
||||||
log::info!("🚀 Starting development server...");
|
log::info!("🚀 Starting development server...");
|
||||||
|
|
||||||
let dist_path = config.out_dir.clone();
|
let dist_path = config.out_dir.clone();
|
||||||
@@ -269,12 +297,26 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| vec![PathBuf::from("src")]);
|
.unwrap_or_else(|| vec![PathBuf::from("src")]);
|
||||||
|
|
||||||
let mut watcher = RecommendedWatcher::new(move |_: notify::Result<notify::Event>| {
|
let watcher_config = config.clone();
|
||||||
log::info!("reload required");
|
let mut watcher = RecommendedWatcher::new(move |info: notify::Result<notify::Event>| {
|
||||||
if chrono::Local::now().timestamp() > last_update_time {
|
let config = watcher_config.clone();
|
||||||
match build_manager.build() {
|
if info.is_ok() {
|
||||||
Ok(_) => last_update_time = chrono::Local::now().timestamp(),
|
if chrono::Local::now().timestamp() > last_update_time {
|
||||||
Err(e) => log::error!("{}", e),
|
match build_manager.rebuild() {
|
||||||
|
Ok(res) => {
|
||||||
|
last_update_time = chrono::Local::now().timestamp();
|
||||||
|
print_console_info(
|
||||||
|
port,
|
||||||
|
&config,
|
||||||
|
PrettierOptions {
|
||||||
|
changed: info.unwrap().paths,
|
||||||
|
warnings: res.warnings,
|
||||||
|
elapsed_time: res.elapsed_time,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => log::error!("{}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -290,7 +332,15 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start serve dev-server at 0.0.0.0
|
// start serve dev-server at 0.0.0.0
|
||||||
log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port);
|
print_console_info(
|
||||||
|
port,
|
||||||
|
&config,
|
||||||
|
PrettierOptions {
|
||||||
|
changed: vec![],
|
||||||
|
warnings: first_build_result.warnings,
|
||||||
|
elapsed_time: first_build_result.elapsed_time,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let file_service_config = config.clone();
|
let file_service_config = config.clone();
|
||||||
let file_service = ServiceBuilder::new()
|
let file_service = ServiceBuilder::new()
|
||||||
@@ -348,6 +398,174 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PrettierOptions {
|
||||||
|
changed: Vec<PathBuf>,
|
||||||
|
warnings: Vec<Diagnostic>,
|
||||||
|
elapsed_time: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) {
|
||||||
|
print!(
|
||||||
|
"{}",
|
||||||
|
String::from_utf8_lossy(
|
||||||
|
&Command::new(if cfg!(target_os = "windows") {
|
||||||
|
"cls"
|
||||||
|
} else {
|
||||||
|
"clear"
|
||||||
|
})
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// for path in &changed {
|
||||||
|
// let path = path
|
||||||
|
// .strip_prefix(crate::crate_root().unwrap())
|
||||||
|
// .unwrap()
|
||||||
|
// .to_path_buf();
|
||||||
|
// log::info!("Updated {}", format!("{}", path.to_str().unwrap()).green());
|
||||||
|
// }
|
||||||
|
|
||||||
|
let mut profile = if config.release { "Release" } else { "Debug" }.to_string();
|
||||||
|
if config.custom_profile.is_some() {
|
||||||
|
profile = config.custom_profile.as_ref().unwrap().to_string();
|
||||||
|
}
|
||||||
|
let hot_reload = if config.hot_reload { "RSX" } else { "Normal" };
|
||||||
|
let crate_root = crate::cargo::crate_root().unwrap();
|
||||||
|
let custom_html_file = if crate_root.join("index.html").is_file() {
|
||||||
|
"Custom [index.html]"
|
||||||
|
} else {
|
||||||
|
"Default"
|
||||||
|
};
|
||||||
|
let url_rewrite = if config
|
||||||
|
.dioxus_config
|
||||||
|
.web
|
||||||
|
.watcher
|
||||||
|
.index_on_404
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
"True"
|
||||||
|
} else {
|
||||||
|
"False"
|
||||||
|
};
|
||||||
|
|
||||||
|
if options.changed.len() <= 0 {
|
||||||
|
println!(
|
||||||
|
"{} @ v{}\n",
|
||||||
|
"Dioxus".bold().green(),
|
||||||
|
crate::DIOXUS_CLI_VERSION,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Project Reloaded: {}\n",
|
||||||
|
format!(
|
||||||
|
"Changed {} files. [{}]",
|
||||||
|
options.changed.len(),
|
||||||
|
chrono::Local::now().format("%H:%M:%S").to_string().dimmed()
|
||||||
|
)
|
||||||
|
.purple()
|
||||||
|
.bold()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"\t> Local : {}",
|
||||||
|
format!("http://localhost:{}/", port).blue()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"\t> NetWork : {}",
|
||||||
|
format!(
|
||||||
|
"http://{}:{}/",
|
||||||
|
get_ip().unwrap_or(String::from("0.0.0.0")),
|
||||||
|
port
|
||||||
|
)
|
||||||
|
.blue()
|
||||||
|
);
|
||||||
|
println!("");
|
||||||
|
println!("\t> Profile : {}", profile.green());
|
||||||
|
println!("\t> Hot Reload : {}", hot_reload.cyan());
|
||||||
|
println!("\t> Index Template : {}", custom_html_file.green());
|
||||||
|
println!("\t> URL Rewrite [index_on_404] : {}", url_rewrite.purple());
|
||||||
|
println!("");
|
||||||
|
println!(
|
||||||
|
"\t> Build Time Use : {} millis",
|
||||||
|
options.elapsed_time.to_string().green().bold()
|
||||||
|
);
|
||||||
|
println!("");
|
||||||
|
|
||||||
|
if options.warnings.len() == 0 {
|
||||||
|
log::info!("{}\n", "A perfect compilation!".green().bold());
|
||||||
|
} else {
|
||||||
|
log::warn!(
|
||||||
|
"{}",
|
||||||
|
format!(
|
||||||
|
"There were {} warning messages during the build: ",
|
||||||
|
options.warnings.len() - 1
|
||||||
|
)
|
||||||
|
.yellow()
|
||||||
|
.bold()
|
||||||
|
);
|
||||||
|
for info in &options.warnings {
|
||||||
|
let message = info.message.clone();
|
||||||
|
if message == format!("{} warnings emitted", options.warnings.len() - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut console = String::new();
|
||||||
|
for span in &info.spans {
|
||||||
|
let file = &span.file_name;
|
||||||
|
let line = (span.line_start, span.line_end);
|
||||||
|
let line_str = if line.0 == line.1 {
|
||||||
|
line.0.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}~{}", line.0, line.1)
|
||||||
|
};
|
||||||
|
let code = span.text.clone();
|
||||||
|
let span_info = if code.len() == 1 {
|
||||||
|
let code = code.get(0).unwrap().text.trim().blue().bold().to_string();
|
||||||
|
format!(
|
||||||
|
"[{}: {}]: '{}' --> {}",
|
||||||
|
file,
|
||||||
|
line_str,
|
||||||
|
code,
|
||||||
|
message.yellow().bold()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let code = code
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(_i, s)| format!("\t{}\n", s.text).blue().bold().to_string())
|
||||||
|
.collect::<String>();
|
||||||
|
format!("[{}: {}]:\n{}\n#:{}", file, line_str, code, message)
|
||||||
|
};
|
||||||
|
console = format!("{console}\n\t{span_info}");
|
||||||
|
}
|
||||||
|
println!("{console}");
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"\n{}\n",
|
||||||
|
"Resolving all warnings will help your code run better!".yellow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ip() -> Option<String> {
|
||||||
|
let socket = match UdpSocket::bind("0.0.0.0:0") {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match socket.connect("8.8.8.8:80") {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match socket.local_addr() {
|
||||||
|
Ok(addr) => return Some(addr.ip().to_string()),
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async fn ws_handler(
|
async fn ws_handler(
|
||||||
ws: WebSocketUpgrade,
|
ws: WebSocketUpgrade,
|
||||||
_: Option<TypedHeader<headers::UserAgent>>,
|
_: Option<TypedHeader<headers::UserAgent>>,
|
||||||
|
|||||||
Reference in new issue
Block a user