diff --git a/Cargo.toml b/Cargo.toml index 7d8d83606..42cc4858c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,8 @@ log = "0.4.13" fern = { version = "0.6.0", features = ["colored"] } wasm-bindgen-cli-support = "0.2.78" anyhow = "1.0.38" - serde = { version = "1.0.133", features = ["derive"] } serde_json = "1" - fs_extra = "1.2.0" cargo_toml = "0.10.0" futures = "0.3.12" @@ -30,6 +28,7 @@ convert_case = "0.5.0" structopt = "0.3.25" cargo_metadata = "0.14.1" tokio = { version = "1.15.0", features = ["full"] } +atty = "0.2.14" [[bin]] path = "src/main.rs" diff --git a/README.md b/README.md index 079f663a3..34be58eda 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-

📦✨ dioxus-Studio

+

📦✨ dioxus-cli

Tooling to supercharge dioxus projects

@@ -10,6 +10,14 @@ # About - dioxus-studio (inspired by wasm-pack and webpack) is a tool to help get dioxus projects off the ground. It handles all the build, development, bundling, and publishing to make web development simple. + +## Installation + +``` +$ cargo install dioxus-cli +``` + +Now, `dioxus` is in your path. + diff --git a/extension/src/extension.ts b/extension/src/extension.ts index c8000694c..c9ab06662 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -13,7 +13,7 @@ export function activate(context: vscode.ExtensionContext) { const selection = editor.selection; const word = document.getText(selection); - const child_proc = spawn("dioxus", ["translate", "-t", word]); + const child_proc = spawn("dioxus", ["translate", "--source", word]); let result = ''; child_proc.stdout?.on('data', data => result += data); @@ -37,7 +37,7 @@ export function activate(context: vscode.ExtensionContext) { const selection = editor.selection; const word = document.getText(selection); - const child_proc = spawn("dioxus", ["translate", "-c", "-t", word]); + const child_proc = spawn("dioxus", ["translate", "--component", "--source", word]); let result = ''; child_proc.stdout?.on('data', data => result += data); diff --git a/out.rsx b/out.rsx new file mode 100644 index 000000000..b30b76f9e --- /dev/null +++ b/out.rsx @@ -0,0 +1,23 @@ + +use dioxus::prelude::*; +fn component(cx: Scope) -> Element { + cx.render(rsx!( + div { + h1 { + "hello" + } + } + )) +} + +use dioxus::prelude::*; +fn component(cx: Scope) -> Element { + cx.render(rsx!( + div { + h1 { + "hello" + } + } + )) +} + diff --git a/packages/rsx-translate/Cargo.toml b/packages/rsx-translate/Cargo.toml new file mode 100644 index 000000000..2af83c634 --- /dev/null +++ b/packages/rsx-translate/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rsx-translate" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/packages/rsx-translate/src/lib.rs b/packages/rsx-translate/src/lib.rs new file mode 100644 index 000000000..1b4a90c93 --- /dev/null +++ b/packages/rsx-translate/src/lib.rs @@ -0,0 +1,8 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +} diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index adb233c33..33a81a540 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -10,3 +10,5 @@ pub struct Build { #[structopt(flatten)] pub build: ConfigOptsBuild, } + +pub async fn perform_build() {} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 55e722d80..fe118cbb7 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -10,38 +10,37 @@ pub mod serve; pub mod translate; pub mod watch; -/// Build, bundle & ship your Rust WASM application to the web. +/// Build, bundle, & ship your Dioxus app. +/// +/// #[derive(StructOpt)] -#[structopt(name = "trunk")] -pub struct Trunk { +#[structopt(name = "dioxus")] +pub struct Cli { #[structopt(subcommand)] - pub action: TrunkSubcommands, - - /// Path to the Trunk config file [default: Trunk.toml] - #[structopt(long, parse(from_os_str), env = "TRUNK_CONFIG")] - pub config: Option, + pub action: Commands, /// Enable verbose logging. #[structopt(short)] pub v: bool, + // + // // note: dioxus is still roughly compatible with trunk + // /// Path to the Trunk config file [default: Trunk.toml] + // #[structopt(long, parse(from_os_str), env = "TRUNK_CONFIG")] + // pub config: Option, } #[derive(StructOpt)] -pub enum TrunkSubcommands { - /// Build the Rust WASM app and all of its assets. - Build(build::Build), - +pub enum Commands { + // /// Build the Rust WASM app and all of its assets. + // Build(build::Build), /// Translate some source file into Dioxus code. Translate(translate::Translate), + // /// Build, watch & serve the Rust WASM app and all of its assets. + // Serve(serve::Serve), - /// Build, watch & serve the Rust WASM app and all of its assets. - /// - /// - Serve(serve::Serve), + // /// Clean output artifacts. + // Clean(clean::Clean), - /// Clean output artifacts. - Clean(clean::Clean), - - /// Trunk config controls. - Config(config::Config), + // /// Trunk config controls. + // Config(config::Config), } diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 1251f9ae6..60a3361d7 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -1,5 +1,7 @@ -use crate::cfg::ConfigOptsBuild; -use anyhow::Result; +use html_parser::Dom; +use html_parser::Node; +use std::fmt::Write; +use std::io::Read; use std::path::PathBuf; use structopt::StructOpt; @@ -10,4 +12,143 @@ pub mod translate; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, StructOpt)] #[structopt(name = "translate")] -pub struct Translate {} +pub struct Translate { + /// Activate debug mode + // short and long flags (-d, --debug) will be deduced from the field's name + #[structopt(short, long)] + pub component: bool, + + /// Input file + #[structopt(short, long)] + pub source: Option, + + /// Input file + #[structopt(short, long, parse(from_os_str))] + pub input: Option, + + /// Output file, stdout if not present + #[structopt(parse(from_os_str))] + pub output: Option, +} + +impl Translate { + pub fn translate(self) -> anyhow::Result<()> { + let Translate { + component: as_component, + input, + output, + source, + } = self; + + let contents = input + .map(|f| { + std::fs::read_to_string(&f) + .unwrap_or_else(|e| panic!("Could not read input file: {}", e)) + }) + .unwrap_or(source.unwrap_or_else(|| { + if atty::is(atty::Stream::Stdin) { + panic!("No input file, source, or stdin to translate from"); + } + + let mut buffer = String::new(); + std::io::stdin().read_to_string(&mut buffer).unwrap(); + + buffer.trim().to_string() + })); + + // parse the input as html and prepare the output + let dom = Dom::parse(&contents)?; + let mut out_buf = String::new(); + + if as_component { + writeln!(out_buf, "use dioxus::prelude::*;")?; + writeln!(out_buf, "fn component(cx: Scope) -> Element {{")?; + writeln!(out_buf, " cx.render(rsx!(")?; + } + for child in &dom.children { + render_child(&mut out_buf, child, if as_component { 2 } else { 0 })?; + } + if as_component { + writeln!(out_buf, " ))")?; + writeln!(out_buf, "}}")?; + } + + if let Some(output) = output { + std::fs::write(&output, out_buf)?; + } else { + print!("{}", out_buf); + } + + Ok(()) + } +} + +fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { + write_tabs(f, il)?; + match child { + Node::Text(t) => writeln!(f, "\"{}\"", t)?, + Node::Comment(e) => writeln!(f, "/* {} */", e)?, + Node::Element(el) => { + // open the tag + write!(f, "{} {{ ", &el.name)?; + + // todo: dioxus will eventually support classnames + // for now, just write them with a space between each + let class_iter = &mut el.classes.iter(); + if let Some(first_class) = class_iter.next() { + write!(f, "class: \"{}", first_class)?; + for next_class in class_iter { + write!(f, " {}", next_class)?; + } + write!(f, "\",")?; + } + write!(f, "\n")?; + + // write the attributes + if let Some(id) = &el.id { + write_tabs(f, il + 1)?; + writeln!(f, "id: \"{}\",", id)?; + } + + for (name, value) in &el.attributes { + write_tabs(f, il + 1)?; + + use convert_case::{Case, Casing}; + if name.chars().any(|ch| ch.is_ascii_uppercase() || ch == '-') { + let new_name = name.to_case(Case::Snake); + match value { + Some(val) => writeln!(f, "{}: \"{}\",", new_name, val)?, + None => writeln!(f, "{}: \"\",", new_name)?, + } + } else { + match name.as_str() { + "for" | "async" | "type" | "as" => write!(f, "r#")?, + _ => {} + } + + match value { + Some(val) => writeln!(f, "{}: \"{}\",", name, val)?, + None => writeln!(f, "{}: \"\",", name)?, + } + } + } + + // now the children + for child in &el.children { + render_child(f, child, il + 1)?; + } + + // close the tag + write_tabs(f, il)?; + writeln!(f, "}}")?; + } + }; + Ok(()) +} + +fn write_tabs(f: &mut impl Write, num: u32) -> std::fmt::Result { + for _ in 0..num { + write!(f, " ")? + } + Ok(()) +} diff --git a/src/cli/translate/translate.rs b/src/cli/translate/translate.rs index c7dad39d4..8b1378917 100644 --- a/src/cli/translate/translate.rs +++ b/src/cli/translate/translate.rs @@ -1,128 +1 @@ -use std::fmt::{Display, Formatter}; -use anyhow::Result; - -use html_parser::{Dom, Node}; - -pub fn translate_from_html_file(target: &str) -> Result { - use std::fs::File; - use std::io::Read; - let mut file = File::open(target).unwrap(); - - let mut contents = String::new(); - file.read_to_string(&mut contents) - .expect("Failed to read your file."); - translate_from_html_to_rsx(&contents, true) -} - -pub fn translate_from_html_to_rsx(html: &str, as_call: bool) -> Result { - let contents = Dom::parse(html)?; - let renderer = RsxRenderer { - as_call, - dom: contents, - }; - Ok(renderer) -} - -pub struct RsxRenderer { - dom: Dom, - as_call: bool, -} - -impl Display for RsxRenderer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.as_call { - writeln!(f, r##"use dioxus::prelude::*;"##)?; - writeln!( - f, - r##" -fn component(cx: Scope) -> Element {{ - cx.render(rsx!( -"## - )?; - } - for child in &self.dom.children { - render_child(f, child, 1)?; - } - if self.as_call { - write!( - f, - r##" - ) -) - "## - )?; - } - Ok(()) - } -} - -fn render_child(f: &mut Formatter<'_>, child: &Node, il: u32) -> std::fmt::Result { - write_tabs(f, il); - match child { - Node::Text(t) => writeln!(f, "\"{}\"", t)?, - Node::Comment(e) => writeln!(f, "/* {} */", e)?, - Node::Element(el) => { - // open the tag - write!(f, "{} {{ ", &el.name)?; - - // todo: dioxus will eventually support classnames - // for now, just write them with a space between each - let class_iter = &mut el.classes.iter(); - if let Some(first_class) = class_iter.next() { - write!(f, "class: \"{}", first_class)?; - for next_class in class_iter { - write!(f, " {}", next_class)?; - } - write!(f, "\",")?; - } - write!(f, "\n")?; - - // write the attributes - if let Some(id) = &el.id { - write_tabs(f, il + 1)?; - writeln!(f, "id: \"{}\",", id)?; - } - - for (name, value) in &el.attributes { - write_tabs(f, il + 1)?; - - use convert_case::{Case, Casing}; - if name.chars().any(|ch| ch.is_ascii_uppercase() || ch == '-') { - let new_name = name.to_case(Case::Snake); - match value { - Some(val) => writeln!(f, "{}: \"{}\",", new_name, val)?, - None => writeln!(f, "{}: \"\",", new_name)?, - } - } else { - match name.as_str() { - "for" | "async" | "type" | "as" => write!(f, "r#")?, - _ => {} - } - - match value { - Some(val) => writeln!(f, "{}: \"{}\",", name, val)?, - None => writeln!(f, "{}: \"\",", name)?, - } - } - } - - // now the children - for child in &el.children { - render_child(f, child, il + 1)?; - } - - // close the tag - write_tabs(f, il)?; - writeln!(f, "}}")?; - } - }; - Ok(()) -} - -fn write_tabs(f: &mut Formatter, num: u32) -> std::fmt::Result { - for _ in 0..num { - write!(f, " ")? - } - Ok(()) -} diff --git a/src/main.rs b/src/main.rs index 043767af6..7ecb50ac7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,49 +1,32 @@ -use dioxus_studio::*; +use dioxus_cli::*; use structopt::StructOpt; #[tokio::main] async fn main() -> Result<()> { - let args = Trunk::from_args(); + let args = Cli::from_args(); set_up_logging(); match args.action { - TrunkSubcommands::Build(_) => { - // + Commands::Translate(opts) => { + opts.translate(); } - TrunkSubcommands::Translate(opts) => { - // let TranslateOptions { - // file, - // text, - // component, - // } = cfg; + // Commands::Build(_) => { + // // + // } - // match component { - // true => { - // let f = helpers::to_component::convert_html_to_component(&text.unwrap())?; - // println!("{}", f); - // } - // false => { - // let renderer = match (file, text) { - // (None, Some(text)) => translate::translate_from_html_to_rsx(&text, false)?, - // (Some(file), None) => translate::translate_from_html_file(&file)?, - // _ => panic!("Must select either file or text - not both or none!"), - // }; - // println!("{}", renderer); - // } - // } - } + // Commands::Clean(_) => { + // // + // } - TrunkSubcommands::Clean(_) => { - // - } - TrunkSubcommands::Config(_) => { - // - } - TrunkSubcommands::Serve(_) => { - // - } + // Commands::Config(_) => { + // // + // } + + // Commands::Serve(_) => { + // // + // } } Ok(()) diff --git a/test.html b/test.html new file mode 100644 index 000000000..33ca3dd1f --- /dev/null +++ b/test.html @@ -0,0 +1,5 @@ +
+

+ hello +

+