This commit is contained in:
Evan Almloff 2022-06-16 13:20:41 -05:00
commit 11d045deeb
11 changed files with 361 additions and 18 deletions

View file

@ -2,7 +2,7 @@
name = "dioxus-cli"
version = "0.1.4"
authors = ["Jonathan Kelley"]
edition = "2018"
edition = "2021"
description = "CLI tool for developing, testing, and publishing Dioxus apps"
license = "MIT/Apache-2.0"
@ -39,11 +39,15 @@ hyper = "0.14.17"
axum = { version = "0.5.1", features = ["ws", "headers"] }
tower-http = { version = "0.2.2", features = ["fs", "trace"] }
headers = "0.3.7"
walkdir = "2"
# tools download
dirs = "4.0.0"
reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns"] }
flate2 = "1.0.22"
tar = "0.4.38"
zip = "0.6.2"
tower = "0.4.12"
syn = { version = "1.0" }

View file

@ -1,8 +1,8 @@
version = "Two"
edition = "2018"
edition = "2021"
imports_granularity = "Crate"
#use_small_heuristics = "Max"
#control_brace_style = "ClosingNextLine"
normalize_comments = true
format_code_in_doc_comments = true
format_code_in_doc_comments = true

View file

@ -42,4 +42,19 @@ script = []
# use binaryen.wasm-opt for output Wasm file
# binaryen just will trigger in `web` platform
binaryen = { wasm_opt = true }
binaryen = { wasm_opt = true }
# use sass auto will auto check sass file and build it.
[application.tools.sass]
# auto will check the assets dirs, and auto to transform all scss file to css file.
input = "*"
# or you can specify some scss file -> css file
# input = [
# # some sass file path
# # this file will translate to `/css/test.css`
# "/css/test.scss"
# ]
source_map = true

View file

@ -1,6 +1,7 @@
use crate::{
config::{CrateConfig, ExecutableType},
error::{Error, Result},
tools::Tool,
DioxusConfig,
};
use std::{
@ -29,6 +30,9 @@ pub fn build(config: &CrateConfig) -> Result<()> {
..
} = config;
// start to build the assets
let ignore_files = build_assets(config)?;
let t_start = std::time::Instant::now();
// [1] Build the .wasm module
@ -44,6 +48,21 @@ pub fn build(config: &CrateConfig) -> Result<()> {
if config.release {
cmd.arg("--release");
}
if config.verbose {
cmd.arg("--verbose");
}
if config.custom_profile.is_some() {
let custom_profile = config.custom_profile.as_ref().unwrap();
cmd.arg("--profile");
cmd.arg(custom_profile);
}
if config.features.is_some() {
let features_str = config.features.as_ref().unwrap().join(" ");
cmd.arg("--features");
cmd.arg(features_str);
}
match executable {
ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
@ -154,6 +173,13 @@ pub fn build(config: &CrateConfig) -> Result<()> {
log::warn!("Error copying dir: {}", _e);
}
}
for ignore in &ignore_files {
let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
let ignore = config.out_dir.join(ignore);
if ignore.is_file() {
std::fs::remove_file(ignore)?;
}
}
}
}
}
@ -166,6 +192,8 @@ pub fn build(config: &CrateConfig) -> Result<()> {
pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
log::info!("🚅 Running build [Desktop] command...");
let ignore_files = build_assets(config)?;
let mut cmd = Command::new("cargo");
cmd.current_dir(&config.crate_dir)
.arg("build")
@ -175,6 +203,21 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
if config.release {
cmd.arg("--release");
}
if config.verbose {
cmd.arg("--verbose");
}
if config.custom_profile.is_some() {
let custom_profile = config.custom_profile.as_ref().unwrap();
cmd.arg("--profile");
cmd.arg(custom_profile);
}
if config.features.is_some() {
let features_str = config.features.as_ref().unwrap().join(" ");
cmd.arg("--features");
cmd.arg(features_str);
}
match &config.executable {
crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
@ -250,6 +293,13 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
log::warn!("Error copying dir: {}", e);
}
}
for ignore in &ignore_files {
let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
let ignore = config.out_dir.join(ignore);
if ignore.is_file() {
std::fs::remove_file(ignore)?;
}
}
}
}
}
@ -339,6 +389,150 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
html.replace("{app_title}", &title)
}
// this function will build some assets file
// like sass tool resources
// this function will return a array which file don't need copy to out_dir.
fn build_assets(config: &CrateConfig) -> Result<Vec<PathBuf>> {
let mut result = vec![];
let dioxus_config = &config.dioxus_config;
let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default();
// check sass tool state
let sass = Tool::Sass;
if sass.is_installed() && dioxus_tools.contains_key("sass") {
let sass_conf = dioxus_tools.get("sass").unwrap();
if let Some(tab) = sass_conf.as_table() {
let source_map = tab.contains_key("source_map");
let source_map = if source_map && tab.get("source_map").unwrap().is_bool() {
if tab.get("source_map").unwrap().as_bool().unwrap_or_default() {
"--source-map"
} else {
"--no-source-map"
}
} else {
"--source-map"
};
if tab.contains_key("input") {
if tab.get("input").unwrap().is_str() {
let file = tab.get("input").unwrap().as_str().unwrap().trim();
if file == "*" {
// if the sass open auto, we need auto-check the assets dir.
let asset_dir = config.asset_dir.clone();
if asset_dir.is_dir() {
for entry in walkdir::WalkDir::new(&asset_dir)
.into_iter()
.filter_map(|e| e.ok())
{
let temp = entry.path();
if temp.is_file() {
let suffix = temp.extension();
if suffix.is_none() { continue; }
let suffix = suffix.unwrap().to_str().unwrap();
if suffix == "scss" || suffix == "sass" {
// if file suffix is `scss` / `sass` we need transform it.
let out_file = format!(
"{}.css",
temp.file_stem().unwrap().to_str().unwrap()
);
let target_path = config
.out_dir
.join(
temp.strip_prefix(&asset_dir)
.unwrap()
.parent()
.unwrap(),
)
.join(out_file);
let res = sass.call(
"sass",
vec![
temp.to_str().unwrap(),
target_path.to_str().unwrap(),
source_map,
],
);
if res.is_ok() {
result.push(temp.to_path_buf());
}
}
}
}
}
} else {
// just transform one file.
let relative_path = if &file[0..1] == "/" {
&file[1..file.len()]
} else {
file
};
let path = config.asset_dir.join(relative_path);
let out_file =
format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
let target_path = config
.out_dir
.join(PathBuf::from(relative_path).parent().unwrap())
.join(out_file);
if path.is_file() {
let res = sass.call(
"sass",
vec![
path.to_str().unwrap(),
target_path.to_str().unwrap(),
source_map,
],
);
if res.is_ok() {
result.push(path);
} else {
log::error!("{:?}", res);
}
}
}
} else if tab.get("input").unwrap().is_array() {
// check files list.
let list = tab.get("input").unwrap().as_array().unwrap();
for i in list {
if i.is_str() {
let path = i.as_str().unwrap();
let relative_path = if &path[0..1] == "/" {
&path[1..path.len()]
} else {
path
};
let path = config.asset_dir.join(relative_path);
let out_file =
format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
let target_path = config
.out_dir
.join(PathBuf::from(relative_path).parent().unwrap())
.join(out_file);
if path.is_file() {
let res = sass.call(
"sass",
vec![
path.to_str().unwrap(),
target_path.to_str().unwrap(),
source_map,
],
);
if res.is_ok() {
result.push(path);
}
}
}
}
}
}
}
}
// SASS END
Ok(result)
}
// use binary_install::{Cache, Download};
// /// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a

View file

@ -14,11 +14,16 @@ impl Build {
// change the release state.
crate_config.with_release(self.build.release);
crate_config.with_verbose(self.build.verbose);
if self.build.example.is_some() {
crate_config.as_example(self.build.example.unwrap());
}
if self.build.profile.is_some() {
crate_config.set_profile(self.build.profile.unwrap());
}
let platform = self.build.platform.unwrap_or_else(|| {
crate_config
.dioxus_config

View file

@ -12,13 +12,26 @@ pub struct ConfigOptsBuild {
#[serde(default)]
pub release: bool,
// Use verbose output [default: false]
#[clap(long)]
#[serde(default)]
pub verbose: bool,
/// Build a example [default: ""]
#[clap(long)]
pub example: Option<String>,
/// Build with custom profile
#[clap(long)]
pub profile: Option<String>,
/// Build platform: support Web & Desktop [default: "default_platform"]
#[clap(long)]
pub platform: Option<String>,
/// Space separated list of features to activate
#[clap(long)]
pub features: Option<Vec<String>>,
}
#[derive(Clone, Debug, Default, Deserialize, Parser)]
@ -36,6 +49,15 @@ pub struct ConfigOptsServe {
#[serde(default)]
pub release: bool,
// Use verbose output [default: false]
#[clap(long)]
#[serde(default)]
pub verbose: bool,
/// Build with custom profile
#[clap(long)]
pub profile: Option<String>,
/// Build platform: support Web & Desktop [default: "default_platform"]
#[clap(long)]
pub platform: Option<String>,
@ -44,6 +66,10 @@ pub struct ConfigOptsServe {
#[clap(long)]
#[serde(default)]
pub hot_reload: bool,
/// Space separated list of features to activate
#[clap(long)]
pub features: Option<Vec<String>>,
}
/// Ensure the given value for `--public-url` is formatted correctly.

View file

@ -18,14 +18,18 @@ impl Serve {
let mut crate_config = crate::CrateConfig::new()?;
// change the relase state.
crate_config
.with_release(self.serve.release)
.with_hot_reload(self.serve.hot_reload);
crate_config.with_hot_reload(self.serve.hot_reload);
crate_config.with_release(self.serve.release);
crate_config.with_verbose(self.serve.verbose);
if self.serve.example.is_some() {
crate_config.as_example(self.serve.example.unwrap());
}
if self.serve.profile.is_some() {
crate_config.set_profile(self.serve.profile.unwrap());
}
let platform = self.serve.platform.unwrap_or_else(|| {
crate_config
.dioxus_config

View file

@ -20,14 +20,18 @@ impl Tool {
Tool::List {} => {
for item in tools::tool_list() {
if tools::Tool::from_str(item).unwrap().is_installed() {
println!("{item} [installed]");
println!("- {item} [installed]");
} else {
println!("{item}");
println!("- {item}");
}
}
}
Tool::AppPath {} => {
println!("{}", tools::tools_path().to_str().unwrap());
if let Some(v) = tools::tools_path().to_str() {
println!("{}", v);
} else {
log::error!("Tools path get failed.");
}
}
Tool::Add { name } => {
let tool_list = tools::tool_list();
@ -58,7 +62,6 @@ impl Tool {
log::info!("Tool {name} install successfully!");
}
}
Ok(())
}
}

View file

@ -115,6 +115,9 @@ pub struct CrateConfig {
pub dioxus_config: DioxusConfig,
pub release: bool,
pub hot_reload: bool,
pub verbose: bool,
pub custom_profile: Option<String>,
pub features: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
@ -164,6 +167,9 @@ impl CrateConfig {
let release = false;
let hot_reload = false;
let verbose = false;
let custom_profile = None;
let features = None;
Ok(Self {
out_dir,
@ -176,6 +182,9 @@ impl CrateConfig {
release,
dioxus_config,
hot_reload,
custom_profile,
features,
verbose,
})
}
@ -194,6 +203,21 @@ impl CrateConfig {
self
}
pub fn with_verbose(&mut self, verbose: bool) -> &mut Self {
self.verbose = verbose;
self
}
pub fn set_profile(&mut self, profile: String) -> &mut Self {
self.custom_profile = Some(profile);
self
}
pub fn set_features(&mut self, features: Vec<String>) -> &mut Self {
self.features = Some(features);
self
}
// pub fn with_build_options(&mut self, options: &BuildOptions) {
// if let Some(name) = &options.example {
// self.as_example(name.clone());

View file

@ -189,7 +189,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> {
let file_service_config = config.clone();
let file_service = ServiceBuilder::new()
.and_then(
|response: Response<ServeFileSystemResponseBody>| async move {
move |response: Response<ServeFileSystemResponseBody>| async move {
let response = if file_service_config
.dioxus_config
.web
@ -300,7 +300,7 @@ pub async fn startup_default(config: CrateConfig) -> Result<()> {
let file_service_config = config.clone();
let file_service = ServiceBuilder::new()
.and_then(
|response: Response<ServeFileSystemResponseBody>| async move {
move |response: Response<ServeFileSystemResponseBody>| async move {
let response = if file_service_config
.dioxus_config
.web

View file

@ -1,6 +1,6 @@
use std::{
fs::{create_dir_all, File},
path::PathBuf,
path::{Path, PathBuf},
process::Command,
};
@ -13,10 +13,11 @@ use tokio::io::AsyncWriteExt;
#[derive(Debug, PartialEq, Eq)]
pub enum Tool {
Binaryen,
Sass,
}
pub fn tool_list() -> Vec<&'static str> {
vec!["binaryen"]
vec!["binaryen", "sass"]
}
pub fn app_path() -> PathBuf {
@ -52,6 +53,7 @@ impl Tool {
pub fn from_str(name: &str) -> Option<Self> {
match name {
"binaryen" => Some(Self::Binaryen),
"sass" => Some(Self::Sass),
_ => None,
}
}
@ -60,6 +62,7 @@ impl Tool {
pub fn name(&self) -> &str {
match self {
Self::Binaryen => "binaryen",
Self::Sass => "sass",
}
}
@ -67,6 +70,7 @@ impl Tool {
pub fn bin_path(&self) -> &str {
match self {
Self::Binaryen => "bin",
Self::Sass => ".",
}
}
@ -84,6 +88,17 @@ impl Tool {
panic!("unsupported platformm");
}
}
Self::Sass => {
if cfg!(target_os = "windows") {
"windows"
} else if cfg!(target_os = "macos") {
"macos"
} else if cfg!(target_os = "linux") {
"linux"
} else {
panic!("unsupported platformm");
}
}
}
}
@ -96,6 +111,13 @@ impl Tool {
target = self.target_platform()
)
}
Self::Sass => {
format!(
"https://github.com/sass/dart-sass/releases/download/1.51.0/dart-sass-1.51.0-{target}-x64.{extension}",
target = self.target_platform(),
extension = self.extension()
)
}
}
}
@ -103,6 +125,13 @@ impl Tool {
pub fn extension(&self) -> &str {
match self {
Self::Binaryen => "tar.gz",
Self::Sass => {
if cfg!(target_os = "windows") {
"zip"
} else {
"tar.gz"
}
}
}
}
@ -131,7 +160,7 @@ impl Tool {
let chunk = chunk_res.context("error reading chunk from download")?;
let _ = file.write(chunk.as_ref()).await;
}
// log::info!("temp file path: {:?}", temp_out);
Ok(temp_out)
}
@ -143,7 +172,7 @@ impl Tool {
let dir_name = if self == &Tool::Binaryen {
"binaryen-version_105"
} else {
""
"dart-sass"
};
if self.extension() == "tar.gz" {
@ -151,7 +180,10 @@ impl Tool {
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack(&tool_path)?;
// println!("{:?} -> {:?}", tool_path.join(dir_name), tool_path.join(self.name()));
std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?;
} else if self.extension() == "zip" {
// decompress the `zip` file
extract_zip(&temp_path, &tool_path)?;
std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?;
}
@ -169,6 +201,13 @@ impl Tool {
command.to_string()
}
}
Tool::Sass => {
if cfg!(target_os = "windows") {
format!("{}.bat", command)
} else {
command.to_string()
}
}
};
if !bin_path.join(&command_file).is_file() {
@ -185,3 +224,32 @@ impl Tool {
Ok(output.stdout)
}
}
fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> {
let zip_file = std::fs::File::open(&file)?;
let mut zip = zip::ZipArchive::new(zip_file)?;
if !target.exists() {
let _ = std::fs::create_dir_all(target)?;
}
for i in 0..zip.len() {
let mut file = zip.by_index(i)?;
if file.is_dir() {
// dir
let target = target.join(Path::new(&file.name().replace('\\', "")));
let _ = std::fs::create_dir_all(target)?;
} else {
// file
let file_path = target.join(Path::new(file.name()));
let mut target_file = if !file_path.exists() {
std::fs::File::create(file_path)?
} else {
std::fs::File::open(file_path)?
};
let _num = std::io::copy(&mut file, &mut target_file)?;
}
}
Ok(())
}