diff --git a/Dioxus.toml b/Dioxus.toml index c3e0dd8a0..e7d144ed5 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -42,4 +42,7 @@ script = [] # use binaryen.wasm-opt for output Wasm file # binaryen just will trigger in `web` platform -binaryen = { wasm_opt = true } \ No newline at end of file +binaryen = { wasm_opt = true } + +# enable tailwind-css CSS building +tailwind-css= { input = "/somewhere", config = "yay"} \ No newline at end of file diff --git a/docs/src/configure.md b/docs/src/configure.md index f274ce70d..7fbf2f546 100644 --- a/docs/src/configure.md +++ b/docs/src/configure.md @@ -25,6 +25,30 @@ We use `toml` to define some info for `dioxus` project. asset_dir = "public" ``` +### Application.Tool + +You can combine different tools with `dioxus`. + +1. ***binaryen*** - Use the `binaryen` tooling suite. + ``` + # current support: wasm-opt + # default: web + binaryen = { wasm_opt = true } + ``` + Use the `wasm_opt = true` key/pair value to activate optimization with wasm-opt. + When building on `release` profile, Dioxus will run `wasm_opt` with `-Oz` option. +2. ***tailwind-css*** - Use the `tailwind-css` standalone binary to generate a Tailwind CSS bundle file. + ``` + tailwind-css = { input = "main.css", config = "tailwind.config.js" } + ``` + You can set two optional keys : + - input: path of the input CSS file (default value is "public/tailwind.css") + - config: path to the config file for Tailwind (default value is "src/tailwind.config.js") + + When building on `release` profile, Dioxus will run `tailwind-css` with the `--minify` option. + + Note : Dioxus will automatically include the generated tailwind file in the `index.html` + ### Web.App Web platform application config: diff --git a/src/builder.rs b/src/builder.rs index 5cacee333..4fd513490 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -28,7 +28,8 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { // [2] Generate the appropriate build folders // [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm // [4] Wasm-opt the .wasm file with whatever optimizations need to be done - // [5] Link up the html page to the wasm module + // [5][OPTIONAL] Builds the Tailwind CSS file using the Tailwind standalone binary + // [6] Link up the html page to the wasm module let CrateConfig { out_dir, @@ -137,18 +138,23 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { if sub.contains_key("wasm_opt") && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false) { + log::info!("Optimizing WASM size with wasm-opt..."); let target_file = out_dir .join("assets") .join("dioxus") .join(format!("{}_bg.wasm", dioxus_config.application.name)); if target_file.is_file() { + let mut args = vec![ + target_file.to_str().unwrap(), + "-o", + target_file.to_str().unwrap(), + ]; + if config.release == true { + args.push("-Oz"); + } binaryen.call( "wasm-opt", - vec![ - target_file.to_str().unwrap(), - "-o", - target_file.to_str().unwrap(), - ], + args, )?; } } @@ -160,6 +166,45 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { } } + // [5][OPTIONAL] If tailwind is enabled and installed we run it to generate the CSS + if dioxus_tools.contains_key("tailwind-css") { + let info = dioxus_tools.get("tailwind-css").unwrap(); + let tailwind = crate::tools::Tool::Tailwind; + + if tailwind.is_installed() { + if let Some(sub) = info.as_table() { + log::info!("Building Tailwind bundle CSS file..."); + + let input_path = match sub.get("input") { + Some(val) => val.as_str().unwrap(), + None => "./public" + }; + let config_path = match sub.get("config") { + Some(val) => val.as_str().unwrap(), + None => "./src/tailwind.config.js" + }; + let mut args = vec![ + "-i", + input_path, + "-o", + "dist/tailwind.css", + "-c", + config_path + ]; + + if config.release == true { + args.push("--minify"); + } + + tailwind.call("tailwind-css", args)?; + } + } else { + log::warn!( + "Tailwind tool not found, you can use `dioxus tool add tailwind-css` to install it." + ); + } + } + // this code will copy all public file to the output dir let copy_options = fs_extra::dir::CopyOptions { overwrite: true, @@ -414,6 +459,9 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { &style.to_str().unwrap(), )) } + if config.application.tools.clone().unwrap_or_default().contains_key("tailwind-css") { + style_str.push_str("\n"); + } html = html.replace("{style_include}", &style_str); let mut script_str = String::new(); diff --git a/src/config.rs b/src/config.rs index bd27a3624..3952f973e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,17 +38,17 @@ impl Default for DioxusConfig { sub_package: None, }, web: WebConfig { - app: WebAppConfing { + app: WebAppConfig { title: Some("dioxus | ⛺".into()), base_path: None, }, - watcher: WebWatcherConfing { + watcher: WebWatcherConfig { watch_path: Some(vec![PathBuf::from("src")]), reload_html: Some(false), index_on_404: Some(true), }, - resource: WebResourceConfing { - dev: WebDevResourceConfing { + resource: WebResourceConfig { + dev: WebDevResourceConfig { style: Some(vec![]), script: Some(vec![]), }, @@ -72,33 +72,33 @@ pub struct ApplicationConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebConfig { - pub app: WebAppConfing, - pub watcher: WebWatcherConfing, - pub resource: WebResourceConfing, + pub app: WebAppConfig, + pub watcher: WebWatcherConfig, + pub resource: WebResourceConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebAppConfing { +pub struct WebAppConfig { pub title: Option, pub base_path: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebWatcherConfing { +pub struct WebWatcherConfig { pub watch_path: Option>, pub reload_html: Option, pub index_on_404: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebResourceConfing { - pub dev: WebDevResourceConfing, +pub struct WebResourceConfig { + pub dev: WebDevResourceConfig, pub style: Option>, pub script: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebDevResourceConfing { +pub struct WebDevResourceConfig { pub style: Option>, pub script: Option>, } diff --git a/src/tools.rs b/src/tools.rs index 2ab358d6b..385a16436 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -2,6 +2,7 @@ use std::{ fs::{create_dir_all, File}, path::{Path, PathBuf}, process::Command, + io::{Read, Write} }; use anyhow::Context; @@ -14,10 +15,11 @@ use tokio::io::AsyncWriteExt; pub enum Tool { Binaryen, Sass, + Tailwind, } pub fn tool_list() -> Vec<&'static str> { - vec!["binaryen", "sass"] + vec!["binaryen", "sass", "tailwind-css"] } pub fn app_path() -> PathBuf { @@ -54,6 +56,7 @@ impl Tool { match name { "binaryen" => Some(Self::Binaryen), "sass" => Some(Self::Sass), + "tailwind-css" => Some(Self::Tailwind), _ => None, } } @@ -63,6 +66,7 @@ impl Tool { match self { Self::Binaryen => "binaryen", Self::Sass => "sass", + Self::Tailwind => "tailwind-css", } } @@ -71,6 +75,7 @@ impl Tool { match self { Self::Binaryen => "bin", Self::Sass => ".", + Self::Tailwind => ".", } } @@ -99,6 +104,32 @@ impl Tool { panic!("unsupported platformm"); } } + Self::Tailwind => { + if cfg!(target_os = "windows") { + "windows" + } else if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "linux") { + "linux" + } else { + panic!("unsupported platformm"); + } + } + } + } + + /// get tool version + pub fn tool_version(&self) -> &str { + match self { + Self::Binaryen => { + "version_105" + } + Self::Sass => { + "1.51.0" + } + Self::Tailwind => { + "v3.1.6" + } } } @@ -107,17 +138,31 @@ impl Tool { match self { Self::Binaryen => { format!( - "https://github.com/WebAssembly/binaryen/releases/download/version_105/binaryen-version_105-x86_64-{target}.tar.gz", + "https://github.com/WebAssembly/binaryen/releases/download/{version}/binaryen-{version}-x86_64-{target}.tar.gz", + version = self.tool_version(), 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}", + "https://github.com/sass/dart-sass/releases/download/{version}/dart-sass-{version}-{target}-x64.{extension}", + version = self.tool_version(), target = self.target_platform(), extension = self.extension() ) } + Self::Tailwind => { + let windows_extension = match self.target_platform() { + "windows" => ".exe", + _ => "" + }; + format!( + "https://github.com/tailwindlabs/tailwindcss/releases/download/{version}/tailwindcss-{target}-x64{optional_ext}", + version = self.tool_version(), + target = self.target_platform(), + optional_ext = windows_extension + ) + } } } @@ -132,6 +177,7 @@ impl Tool { "tar.gz" } } + Self::Tailwind => "bin" } } @@ -169,10 +215,10 @@ impl Tool { let temp_path = self.temp_out_path(); let tool_path = tools_path(); - let dir_name = if self == &Tool::Binaryen { - "binaryen-version_105" - } else { - "dart-sass" + let dir_name = match self { + Self::Binaryen => format!("binaryen-{}", self.tool_version()), + Self::Sass => "dart-sass".to_string(), + Self::Tailwind => self.name().to_string() }; if self.extension() == "tar.gz" { @@ -185,6 +231,37 @@ impl Tool { // decompress the `zip` file extract_zip(&temp_path, &tool_path)?; std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?; + } else if self.extension() == "bin" { + let bin_path = match self.target_platform() { + "windows" => tool_path.join(&dir_name).join(self.name()).join(".exe"), + _ => tool_path.join(&dir_name).join(self.name()) + } ; + // Manualy creating tool directory because we directly download the binary via Github + std::fs::create_dir( tool_path.join(dir_name))?; + + let mut final_file = std::fs::File::create(&bin_path)?; + let mut temp_file = File::open(&temp_path)?; + let mut content = Vec::new(); + + temp_file.read_to_end(&mut content)?; + final_file.write_all(&content)?; + + if self.target_platform() == "linux" { + // This code does not update permissions idk why + /*let mut perms = final_file.metadata()?.permissions(); + perms.set_mode(0o744);*/ + + // Adding to the binary execution rights with "chmod" + let mut command = Command::new("chmod"); + + let _ = command + .args(vec!["+x", bin_path.to_str().unwrap()]) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + .output()?; + } + + std::fs::remove_file(&temp_path)?; } Ok(()) @@ -208,6 +285,13 @@ impl Tool { command.to_string() } } + Tool::Tailwind => { + if cfg!(target_os = "windows") { + format!("{}.exe", command) + } else { + command.to_string() + } + } }; if !bin_path.join(&command_file).is_file() {