From aebf6678a8ea81ef48ac24c0a259130d9ab9e42e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 7 Jul 2021 16:54:14 -0400 Subject: [PATCH 001/516] wip: move out of dioxus main --- .gitignore | 2 + .vscode/settings.json | 3 + Cargo.toml | 33 ++++++++++ README.md | 48 ++++++++++++++ src/builder.rs | 146 ++++++++++++++++++++++++++++++++++++++++++ src/cargo.rs | 69 ++++++++++++++++++++ src/cli.rs | 62 ++++++++++++++++++ src/config.rs | 92 ++++++++++++++++++++++++++ src/develop.rs | 111 ++++++++++++++++++++++++++++++++ src/error.rs | 34 ++++++++++ src/lib.rs | 8 +++ src/logging.rs | 49 ++++++++++++++ src/main.rs | 28 ++++++++ src/watch.rs | 11 ++++ 14 files changed, 696 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/builder.rs create mode 100644 src/cargo.rs create mode 100644 src/cli.rs create mode 100644 src/config.rs create mode 100644 src/develop.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/logging.rs create mode 100644 src/main.rs create mode 100644 src/watch.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..96ef6c0b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..80f51cff8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.inlayHints.enable": false +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..299a26ae6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "dioxus-studio" +version = "0.1.0" +authors = ["Jonathan Kelley "] +edition = "2018" +description = "CLI tool for developing, testing, and publishing Dioxus apps" +"license" = "MIT/Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror = "1.0.23" +log = "0.4.13" +fern = { version="0.6.0", features=["colored"] } +wasm-bindgen-cli-support = "0.2.73" +anyhow = "1.0.38" +argh = "0.1.4" +serde = "1.0.120" +serde_json = "1.0.61" +async-std = { version="1.9.0", features=["attributes"] } +tide = "0.15.0" +fs_extra = "1.2.0" + +cargo_toml = "0.8.1" +futures = "0.3.12" +notify = "5.0.0-pre.4" +rjdebounce = "0.2.1" +tempfile = "3.2.0" + +[[bin]] + +path = "src/main.rs" +name = "dioxus" diff --git a/README.md b/README.md new file mode 100644 index 000000000..bf2e3310a --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +
+

📦✨ dioxus-cli

+

+ Tooling to supercharge dioxus projects +

+
+ +# About +--- +dioxus-cli (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 just a simple two commands: `cargo init` and `dioxus-cli publish`. + +Best thing: +- No NPM. +- No Webpack. +- No `node_modules`. +- No Babel +- No parcel +- No rollup +- No ESLint + +Just install Rust, dioxus-cli, and you're good to go. +`cargo install --git github.com/jkelleyrtp/dioxus-cli` + +Need a development server? +`dioxus develop` + +Need to run an example? +`dioxus develop --example textbox` + +Need to benchmark a component? +`dioxus bench` + +Need to test your code? +`dioxus test` + +Need to build your code into a bundle? +`dioxus build --outdir public` + +Need to publish your code to GitHub pages, Netlify, etc? +`dioxus publish --ghpages myrepo.git` + +# Use in your project +--- +Sometimes you'll want to include static assets without bundling them into your .wasm content. dioxus-cli provides a few ways of doing this: + +- Load in dynamic content using `dioxus::asset("./static/images/blah.svg")` +- Live-reload HTML templates without rebuilding your .wasm with `dioxus::template("./templates/blah.html")` +- Use a CSS library like tailwind in your dioxus configuration with diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 000000000..1d06b3ae8 --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,146 @@ +use crate::{ + cli::BuildOptions, + config::{Config, ExecutableType}, + error::Result, +}; +use log::{info, warn}; +use std::{io::Write, process::Command}; +use wasm_bindgen_cli_support::Bindgen; + +pub struct BuildConfig {} +impl Into for BuildOptions { + fn into(self) -> BuildConfig { + BuildConfig {} + } +} +impl Default for BuildConfig { + fn default() -> Self { + Self {} + } +} + +pub fn build(config: &Config, _build_config: &BuildConfig) -> Result<()> { + /* + [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 + [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 + */ + + let Config { + out_dir, + crate_dir, + target_dir, + static_dir, + executable, + .. + } = config; + + let t_start = std::time::Instant::now(); + + // [1] Build the .wasm module + info!("Running build commands..."); + let mut cmd = Command::new("cargo"); + cmd.current_dir(&crate_dir) + .arg("build") + .arg("--target") + .arg("wasm32-unknown-unknown") + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()); + + if config.release { + cmd.arg("--release"); + } + + match executable { + ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), + ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), + ExecutableType::Example(name) => cmd.arg("--example").arg(name), + }; + + let mut child = cmd.spawn()?; + let _err_code = child.wait()?; + + info!("Build complete!"); + + // [2] Establish the output directory structure + let bindgen_outdir = out_dir.join("wasm"); + + // [3] Bindgen the final binary for use easy linking + let mut bindgen_builder = Bindgen::new(); + + let release_type = match config.release { + true => "release", + false => "debug", + }; + + let input_path = match executable { + ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir + // .join("wasm32-unknown-unknown/release") + .join(format!("wasm32-unknown-unknown/{}", release_type)) + .join(format!("{}.wasm", name)), + + ExecutableType::Example(name) => target_dir + // .join("wasm32-unknown-unknown/release/examples") + .join(format!("wasm32-unknown-unknown/{}/examples", release_type)) + .join(format!("{}.wasm", name)), + }; + + bindgen_builder + .input_path(input_path) + .web(true)? + .debug(true) + .demangle(true) + .keep_debug(true) + .remove_name_section(false) + .remove_producers_section(false) + .out_name("module") + .generate(&bindgen_outdir)?; + + // [4] + // TODO: wasm-opt + + // [5] Generate the html file with the module name + // TODO: support names via options + info!("Writing to '{:#?}' directory...", out_dir); + let mut file = std::fs::File::create(out_dir.join("index.html"))?; + file.write_all(gen_page("./wasm/module.js").as_str().as_bytes())?; + + let copy_options = fs_extra::dir::CopyOptions::new(); + match fs_extra::dir::copy(static_dir, out_dir, ©_options) { + Ok(_) => {} + Err(_e) => { + warn!("Error copying dir"); + } + } + + let t_end = std::time::Instant::now(); + log::info!("Done in {}ms! 🎉", (t_end - t_start).as_millis()); + Ok(()) +} + +fn gen_page(module: &str) -> String { + format!( + r#" + + + + + + + + + + + +"#, + module + ) +} diff --git a/src/cargo.rs b/src/cargo.rs new file mode 100644 index 000000000..3a911a81e --- /dev/null +++ b/src/cargo.rs @@ -0,0 +1,69 @@ +//! Utilities for working with cargo and rust files +use crate::error::{Error, Result}; +use std::{env, fs, path::PathBuf, process::Command, str}; + +/// How many parent folders are searched for a `Cargo.toml` +const MAX_ANCESTORS: u32 = 10; + +/// Returns the root of the crate that the command is run from +/// +/// If the command is run from the workspace root, this will return the top-level Cargo.toml +pub fn crate_root() -> Result { + // From the current directory we work our way up, looking for `Cargo.toml` + env::current_dir() + .ok() + .and_then(|mut wd| { + for _ in 0..MAX_ANCESTORS { + if contains_manifest(&wd) { + return Some(wd); + } + if !wd.pop() { + break; + } + } + None + }) + .ok_or_else(|| Error::CargoError("Failed to find the cargo directory".to_string())) +} + +/// Returns the root of a workspace +/// TODO @Jon, find a different way that doesn't rely on the cargo metadata command (it's slow) +pub fn workspace_root() -> Result { + let output = Command::new("cargo") + .args(&["metadata"]) + .output() + .map_err(|_| Error::CargoError("Manifset".to_string()))?; + + if !output.status.success() { + let mut msg = str::from_utf8(&output.stderr).unwrap().trim(); + if msg.starts_with("error: ") { + msg = &msg[7..]; + } + + return Err(Error::CargoError(msg.to_string())); + } + + let stdout = str::from_utf8(&output.stdout).unwrap(); + for line in stdout.lines() { + let meta: serde_json::Value = serde_json::from_str(line) + .map_err(|_| Error::CargoError("InvalidOutput".to_string()))?; + + let root = meta["workspace_root"] + .as_str() + .ok_or_else(|| Error::CargoError("InvalidOutput".to_string()))?; + return Ok(root.into()); + } + + Err(Error::CargoError("InvalidOutput".to_string())) +} + +/// Checks if the directory contains `Cargo.toml` +fn contains_manifest(path: &PathBuf) -> bool { + fs::read_dir(path) + .map(|entries| { + entries + .filter_map(Result::ok) + .any(|ent| &ent.file_name() == "Cargo.toml") + }) + .unwrap_or(false) +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 000000000..852d4ee71 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,62 @@ +use argh::FromArgs; + +#[derive(FromArgs, PartialEq, Debug)] +/// Top-level command. +pub struct LaunchOptions { + #[argh(subcommand)] + pub command: LaunchCommand, +} + +/// The various kinds of commands that `wasm-pack` can execute. +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand)] +pub enum LaunchCommand { + Develop(DevelopOptions), + Build(BuildOptions), + Test(TestOptions), + Publish(PublishOptions), +} + +/// Publish your yew application to Github Pages, Netlify, or S3 +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "publish")] +pub struct PublishOptions {} + +/// 🔬 test your yew application! +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "test")] +pub struct TestOptions { + /// an example in the crate + #[argh(option)] + pub example: Option, +} + +/// 🏗️ Build your yew application +#[derive(FromArgs, PartialEq, Debug, Clone)] +#[argh(subcommand, name = "build")] +pub struct BuildOptions { + /// an optional direction which is "up" by default + #[argh(option, short = 'o', default = "String::from(\"public\")")] + pub outdir: String, + + /// an example in the crate + #[argh(option)] + pub example: Option, + + /// develop in release mode + #[argh(switch, short = 'r')] + pub release: bool, +} + +/// 🛠 Start a development server +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "develop")] +pub struct DevelopOptions { + /// an example in the crate + #[argh(option)] + pub example: Option, + + /// develop in release mode + #[argh(switch, short = 'r')] + pub release: bool, +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 000000000..02a04eaf7 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,92 @@ +use crate::{ + cli::{BuildOptions, DevelopOptions}, + error::Result, +}; +use std::{io::Write, path::PathBuf, process::Command}; + +#[derive(Debug, Clone)] +pub struct Config { + pub out_dir: PathBuf, + pub crate_dir: PathBuf, + pub workspace_dir: PathBuf, + pub target_dir: PathBuf, + pub static_dir: PathBuf, + pub manifest: cargo_toml::Manifest, + pub executable: ExecutableType, + pub release: bool, +} + +#[derive(Debug, Clone)] +pub enum ExecutableType { + Binary(String), + Lib(String), + Example(String), +} + +impl Config { + pub fn new() -> Result { + let crate_dir = crate::cargo::crate_root()?; + let workspace_dir = crate::cargo::workspace_root()?; + let target_dir = workspace_dir.join("target"); + let out_dir = crate_dir.join("public"); + let cargo_def = &crate_dir.join("Cargo.toml"); + let static_dir = crate_dir.join("static"); + + let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap(); + + // We just assume they're using a 'main.rs' + // Anyway, we've already parsed the manifest, so it should be easy to change the type + let output_filename = (&manifest) + .lib + .as_ref() + .and_then(|lib| lib.name.clone()) + .or_else(|| { + (&manifest) + .package + .as_ref() + .and_then(|pkg| Some(pkg.name.replace("-", "_"))) + .clone() + }) + .expect("No lib found from cargo metadata"); + let executable = ExecutableType::Binary(output_filename); + + let release = false; + + Ok(Self { + out_dir, + crate_dir, + workspace_dir, + target_dir, + static_dir, + manifest, + executable, + release, + }) + } + + pub fn as_example(&mut self, example_name: String) -> &mut Self { + self.executable = ExecutableType::Example(example_name); + self + } + + pub fn with_release(&mut self, release: bool) -> &mut Self { + self.release = release; + self + } + + pub fn with_build_options(&mut self, options: &BuildOptions) { + if let Some(name) = &options.example { + self.as_example(name.clone()); + } + self.release = options.release; + self.out_dir = options.outdir.clone().into(); + } + + pub fn with_develop_options(&mut self, options: &DevelopOptions) { + if let Some(name) = &options.example { + self.as_example(name.clone()); + } + self.release = options.release; + self.out_dir = tempfile::Builder::new().tempdir().expect("").into_path(); + } +} diff --git a/src/develop.rs b/src/develop.rs new file mode 100644 index 000000000..1ae016b0d --- /dev/null +++ b/src/develop.rs @@ -0,0 +1,111 @@ +use crate::{builder::BuildConfig, cli::DevelopOptions, config::Config, error::Result}; +use async_std::prelude::FutureExt; + +use async_std::future; +use async_std::prelude::*; + +use log::info; +use notify::{RecommendedWatcher, RecursiveMode, Watcher}; +use std::path::PathBuf; +use std::time::Duration; + +pub struct DevelopConfig {} +impl Into for DevelopOptions { + fn into(self) -> DevelopConfig { + DevelopConfig {} + } +} + +pub async fn start(config: &Config, _options: &DevelopConfig) -> Result<()> { + log::info!("Starting development server 🚀"); + + let Config { out_dir, .. } = config; + + // Spawn the server onto a seperate task + // This lets the task progress while we handle file updates + let server = async_std::task::spawn(launch_server(out_dir.clone())); + let watcher = async_std::task::spawn(watch_directory(config.clone())); + + match server.race(watcher).await { + Err(e) => log::warn!("Error running development server, {:?}", e), + _ => {} + } + + Ok(()) +} + +async fn watch_directory(config: Config) -> Result<()> { + // Create a channel to receive the events. + let (watcher_tx, watcher_rx) = async_std::channel::bounded(100); + + // Automatically select the best implementation for your platform. + // You can also access each implementation directly e.g. INotifyWatcher. + let mut watcher: RecommendedWatcher = Watcher::new_immediate(move |res| { + async_std::task::block_on(watcher_tx.send(res)); + }) + .expect("failed to make watcher"); + + let src_dir = crate::cargo::crate_root()?; + + // Add a path to be watched. All files and directories at that path and + // below will be monitored for changes. + watcher + .watch(src_dir.join("src"), RecursiveMode::Recursive) + .expect("Failed to watch dir"); + + watcher + .watch(src_dir.join("examples"), RecursiveMode::Recursive) + .expect("Failed to watch dir"); + + let build_config = BuildConfig::default(); + + 'run: loop { + crate::builder::build(&config, &build_config)?; + + // Wait for the message with a debounce + let _msg = watcher_rx + .recv() + .join(future::ready(1_usize).delay(Duration::from_millis(2000))) + .await; + + info!("File updated, rebuilding app"); + } + Ok(()) +} + +async fn launch_server(outdir: PathBuf) -> Result<()> { + let _crate_dir = crate::cargo::crate_root()?; + let _workspace_dir = crate::cargo::workspace_root()?; + + let mut app = tide::with_state(ServerState::new(outdir.to_owned())); + let p = outdir.display().to_string(); + + app.at("/") + .get(|req: tide::Request| async move { + log::info!("Connected to development server"); + let state = req.state(); + Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?) + }) + .serve_dir(p)?; + + let port = "8080"; + let serve_addr = format!("0.0.0.0:{}", port); + // let serve_addr = format!("127.0.0.1:{}", port); + + info!("App available at http://{}", serve_addr); + app.listen(serve_addr).await?; + Ok(()) +} + +/// https://github.com/http-rs/tide/blob/main/examples/state.rs +/// Tide seems to prefer using state instead of injecting into the app closure +/// The app closure needs to be static and +#[derive(Clone)] +struct ServerState { + serv_path: PathBuf, +} +impl ServerState { + fn new(serv_path: PathBuf) -> Self { + Self { serv_path } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..80a884956 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,34 @@ +use thiserror::Error as ThisError; + +pub type Result = std::result::Result; + +#[derive(ThisError, Debug)] +pub enum Error { + /// Used when errors need to propogate but are too unique to be typed + #[error("{0}")] + Unique(String), + + #[error("I/O Error: {0}")] + IO(#[from] std::io::Error), + + #[error("Failed to write error")] + FailedToWrite, + + #[error("Failed to write error")] + CargoError(String), + + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +impl From<&str> for Error { + fn from(s: &str) -> Self { + Error::Unique(s.to_string()) + } +} + +impl From for Error { + fn from(s: String) -> Self { + Error::Unique(s) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..d6e7c838d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,8 @@ +pub mod builder; +pub mod cargo; +pub mod cli; +pub mod config; +pub mod develop; +pub mod error; +pub mod logging; +pub mod watch; diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 000000000..94a9ce85b --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,49 @@ +use fern::colors::{Color, ColoredLevelConfig}; +use log::debug; + +pub fn set_up_logging() { + // configure colors for the whole line + let colors_line = ColoredLevelConfig::new() + .error(Color::Red) + .warn(Color::Yellow) + // we actually don't need to specify the color for debug and info, they are white by default + .info(Color::White) + .debug(Color::White) + // depending on the terminals color scheme, this is the same as the background color + .trace(Color::BrightBlack); + + // configure colors for the name of the level. + // since almost all of them are the same as the color for the whole line, we + // just clone `colors_line` and overwrite our changes + let colors_level = colors_line.clone().info(Color::Green); + // here we set up our fern Dispatch + fern::Dispatch::new() + .format(move |out, message, record| { + out.finish(format_args!( + "{color_line}[{level}{color_line}] {message}\x1B[0m", + color_line = format_args!( + "\x1B[{}m", + colors_line.get_color(&record.level()).to_fg_str() + ), + level = colors_level.color(record.level()), + message = message, + )); + }) + // set the default log level. to filter out verbose log messages from dependencies, set + // this to Warn and overwrite the log level for your crate. + .level(log::LevelFilter::Info) + // .level(log::LevelFilter::Warn) + // change log levels for individual modules. Note: This looks for the record's target + // field which defaults to the module path but can be overwritten with the `target` + // parameter: + // `info!(target="special_target", "This log message is about special_target");` + // .level_for("dioxus", log::LevelFilter::Debug) + // .level_for("dioxus", log::LevelFilter::Info) + // .level_for("pretty_colored", log::LevelFilter::Trace) + // output to stdout + .chain(std::io::stdout()) + .apply() + .unwrap(); + + debug!("finished setting up logging! yay!"); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 000000000..7a5df372c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,28 @@ +use diopack::cli::{LaunchCommand, LaunchOptions}; +use dioxus_cli as diopack; + +#[async_std::main] +async fn main() -> diopack::error::Result<()> { + diopack::logging::set_up_logging(); + + let opts: LaunchOptions = argh::from_env(); + let mut config = diopack::config::Config::new()?; + + match opts.command { + LaunchCommand::Build(options) => { + config.with_build_options(&options); + diopack::builder::build(&config, &(options.into()))?; + } + + LaunchCommand::Develop(options) => { + config.with_develop_options(&options); + diopack::develop::start(&config, &(options.into())).await?; + } + + _ => { + todo!("Command not currently implemented"); + } + } + + Ok(()) +} diff --git a/src/watch.rs b/src/watch.rs new file mode 100644 index 000000000..ae39bc670 --- /dev/null +++ b/src/watch.rs @@ -0,0 +1,11 @@ +pub struct Watcher {} + +impl Watcher { + // Spawns a new notify instance that alerts us of changes + pub fn new() -> Self { + Self {} + } + + /// Subsribe to the watcher + pub fn subscribe() {} +} From 7a67964a2fdd81563633635d62f8e3d2e60e0c95 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 7 Jul 2021 16:54:40 -0400 Subject: [PATCH 002/516] wip: rename docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf2e3310a..fc1e5796b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-

📦✨ dioxus-cli

+

📦✨ dioxus-Studio

Tooling to supercharge dioxus projects

From e2a4efeb9ee9ee9d50238514ef7f98eca90a07d4 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 23 Sep 2021 13:33:22 -0400 Subject: [PATCH 003/516] wip: bump versions --- Cargo.toml | 6 +++--- src/develop.rs | 6 +++--- src/main.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 299a26ae6..b57f28140 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,13 +11,13 @@ description = "CLI tool for developing, testing, and publishing Dioxus apps" [dependencies] thiserror = "1.0.23" log = "0.4.13" -fern = { version="0.6.0", features=["colored"] } -wasm-bindgen-cli-support = "0.2.73" +fern = { version = "0.6.0", features = ["colored"] } +wasm-bindgen-cli-support = "0.2.78" anyhow = "1.0.38" argh = "0.1.4" serde = "1.0.120" serde_json = "1.0.61" -async-std = { version="1.9.0", features=["attributes"] } +async-std = { version = "1.9.0", features = ["attributes"] } tide = "0.15.0" fs_extra = "1.2.0" diff --git a/src/develop.rs b/src/develop.rs index 1ae016b0d..48565e311 100644 --- a/src/develop.rs +++ b/src/develop.rs @@ -40,7 +40,7 @@ async fn watch_directory(config: Config) -> Result<()> { // Automatically select the best implementation for your platform. // You can also access each implementation directly e.g. INotifyWatcher. - let mut watcher: RecommendedWatcher = Watcher::new_immediate(move |res| { + let mut watcher: RecommendedWatcher = Watcher::new(move |res| { async_std::task::block_on(watcher_tx.send(res)); }) .expect("failed to make watcher"); @@ -50,11 +50,11 @@ async fn watch_directory(config: Config) -> Result<()> { // Add a path to be watched. All files and directories at that path and // below will be monitored for changes. watcher - .watch(src_dir.join("src"), RecursiveMode::Recursive) + .watch(&src_dir.join("src"), RecursiveMode::Recursive) .expect("Failed to watch dir"); watcher - .watch(src_dir.join("examples"), RecursiveMode::Recursive) + .watch(&src_dir.join("examples"), RecursiveMode::Recursive) .expect("Failed to watch dir"); let build_config = BuildConfig::default(); diff --git a/src/main.rs b/src/main.rs index 7a5df372c..8b7ce240d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -use diopack::cli::{LaunchCommand, LaunchOptions}; -use dioxus_cli as diopack; +use dioxus_studio as diopack; +use dioxus_studio::cli::{LaunchCommand, LaunchOptions}; #[async_std::main] async fn main() -> diopack::error::Result<()> { From f81183e2a6bd12224afa840a233b3fbbeb38c164 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 10 Oct 2021 15:42:16 -0400 Subject: [PATCH 004/516] feat: remove tailwind from tempalte --- src/builder.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 1d06b3ae8..6b61c8665 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -127,10 +127,6 @@ fn gen_page(module: &str) -> String { - @@ -138,6 +134,7 @@ fn gen_page(module: &str) -> String { import init from "{}"; init("./wasm/module_bg.wasm"); +
"#, From 0966653630a5b2ee06fd4a66f98e3c5b4196aa25 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 11:57:30 -0500 Subject: [PATCH 005/516] docs: fix readme --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fc1e5796b..9f436199c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ # About --- -dioxus-cli (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 just a simple two commands: `cargo init` and `dioxus-cli publish`. +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 just a simple two commands: `cargo init` and `dioxus-cli publish`. Best thing: - No NPM. @@ -18,6 +18,17 @@ Best thing: - No rollup - No ESLint + +## Visual Studio +Dioxus Studio features a command-line driven visual interface to manage your project. It's a great way to get started with dioxus projects. + +To start the visual environment, simply type `dioxus studio` in your terminal. + +From there, you can explore all the options to build, bundle, and deploy your app. + + +## Features + Just install Rust, dioxus-cli, and you're good to go. `cargo install --git github.com/jkelleyrtp/dioxus-cli` @@ -33,12 +44,12 @@ Need to benchmark a component? Need to test your code? `dioxus test` +Need to convert some html into rsx? +`dioxus convert file.html >> file.rs` + Need to build your code into a bundle? `dioxus build --outdir public` -Need to publish your code to GitHub pages, Netlify, etc? -`dioxus publish --ghpages myrepo.git` - # Use in your project --- Sometimes you'll want to include static assets without bundling them into your .wasm content. dioxus-cli provides a few ways of doing this: From 5fdb14446bb88cddd89179e909081d757204c924 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 12:03:18 -0500 Subject: [PATCH 006/516] wip: studio upgrades --- .vscode/settings.json | 4 +- Cargo.toml | 9 +- src/builder.rs | 26 ++++-- src/cli.rs | 45 ++++++++-- src/config.rs | 5 +- src/develop.rs | 76 ++++++++++++----- src/err.html | 19 +++++ src/error.rs | 3 + src/lib.rs | 29 +++++-- src/logging.rs | 2 - src/main.rs | 24 +----- src/studio.rs | 194 ++++++++++++++++++++++++++++++++++++++++++ src/translate.rs | 101 ++++++++++++++++++++++ tests/test.html | 32 +++++++ 14 files changed, 499 insertions(+), 70 deletions(-) create mode 100644 src/err.html create mode 100644 src/studio.rs create mode 100644 src/translate.rs create mode 100644 tests/test.html diff --git a/.vscode/settings.json b/.vscode/settings.json index 80f51cff8..1c68f97cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.inlayHints.enable": false -} \ No newline at end of file + "rust-analyzer.inlayHints.enable": true +} diff --git a/Cargo.toml b/Cargo.toml index b57f28140..99d6ff539 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,19 @@ argh = "0.1.4" serde = "1.0.120" serde_json = "1.0.61" async-std = { version = "1.9.0", features = ["attributes"] } -tide = "0.15.0" +tide = "0.16.0" fs_extra = "1.2.0" -cargo_toml = "0.8.1" +cargo_toml = "0.10.0" futures = "0.3.12" notify = "5.0.0-pre.4" rjdebounce = "0.2.1" tempfile = "3.2.0" +html_parser = "0.6.2" + +tui = { version = "0.15.0", features = ["crossterm"] } +crossterm = "0.19.0" +tui-template = { git = "https://github.com/jkelleyrtp/tui-builder.git" } [[bin]] diff --git a/src/builder.rs b/src/builder.rs index 6b61c8665..83a6d1de5 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,10 +1,13 @@ use crate::{ cli::BuildOptions, - config::{Config, ExecutableType}, - error::Result, + config::{CrateConfig, ExecutableType}, + error::{Error, Result}, }; use log::{info, warn}; -use std::{io::Write, process::Command}; +use std::{ + io::{Read, Write}, + process::Command, +}; use wasm_bindgen_cli_support::Bindgen; pub struct BuildConfig {} @@ -19,7 +22,7 @@ impl Default for BuildConfig { } } -pub fn build(config: &Config, _build_config: &BuildConfig) -> Result<()> { +pub fn build(config: &CrateConfig, _build_config: &BuildConfig) -> Result<()> { /* [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 @@ -28,7 +31,7 @@ pub fn build(config: &Config, _build_config: &BuildConfig) -> Result<()> { [5] Link up the html page to the wasm module */ - let Config { + let CrateConfig { out_dir, crate_dir, target_dir, @@ -60,9 +63,16 @@ pub fn build(config: &Config, _build_config: &BuildConfig) -> Result<()> { }; let mut child = cmd.spawn()?; - let _err_code = child.wait()?; + let output = child.wait()?; - info!("Build complete!"); + if output.success() { + info!("Build complete!"); + } else { + log::error!("Build failed!"); + let mut reason = String::new(); + child.stderr.unwrap().read_to_string(&mut reason)?; + return Err(Error::BuildFailed(reason)); + } // [2] Establish the output directory structure let bindgen_outdir = out_dir.join("wasm"); @@ -129,6 +139,8 @@ fn gen_page(module: &str) -> String { +
+
diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index f4b69d5d7..58d797485 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -13,8 +13,7 @@ pub struct Build { impl Build { pub fn build(self) -> anyhow::Result<()> { - - let mut crate_config = crate::CrateConfig::new()?; + let mut crate_config = crate::CrateConfig::new()?; // change the relase state. crate_config.with_release(self.build.release); @@ -23,4 +22,4 @@ impl Build { Ok(()) } -} \ No newline at end of file +} diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index feab0e61f..5736ebc19 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -67,6 +67,26 @@ pub struct ConfigOptsBuild { pub pattern_params: Option>, } +#[derive(Clone, Debug, Default, Deserialize, StructOpt)] +pub struct ConfigOptsServe { + /// The index HTML file to drive the bundling process [default: index.html] + #[structopt(parse(from_os_str))] + pub target: Option, + + /// Build in release mode [default: false] + #[structopt(long)] + #[serde(default)] + pub release: bool, + + /// The output dir for all final assets [default: dist] + #[structopt(short, long, parse(from_os_str))] + pub dist: Option, + + /// The public URL from which assets are to be served [default: /] + #[structopt(long, parse(from_str=parse_public_url))] + pub public_url: Option, +} + /// Ensure the given value for `--public-url` is formatted correctly. pub fn parse_public_url(val: &str) -> String { let prefix = if !val.starts_with('/') { "/" } else { "" }; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index fa3bd2c77..32492459a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -35,7 +35,6 @@ pub enum Commands { Translate(translate::Translate), // /// Build, watch & serve the Rust WASM app and all of its assets. Serve(serve::Serve), - // /// Clean output artifacts. // Clean(clean::Clean), diff --git a/src/cli/serve/develop.rs b/src/cli/serve/develop.rs index dd386b14e..27aba99ee 100644 --- a/src/cli/serve/develop.rs +++ b/src/cli/serve/develop.rs @@ -1,145 +1,145 @@ -use crate::{cli::DevelopOptions, config::CrateConfig, error::Result}; -use async_std::prelude::FutureExt; +// use crate::{cli::DevelopOptions, config::CrateConfig, error::Result}; +// use async_std::prelude::FutureExt; -use log::info; -use notify::{RecommendedWatcher, RecursiveMode, Watcher}; -use std::path::PathBuf; -use std::sync::atomic::AtomicBool; -use std::sync::Arc; -use tide::http::mime::HTML; -use tide::http::Mime; +// use log::info; +// use notify::{RecommendedWatcher, RecursiveMode, Watcher}; +// use std::path::PathBuf; +// use std::sync::atomic::AtomicBool; +// use std::sync::Arc; +// use tide::http::mime::HTML; +// use tide::http::Mime; -pub struct DevelopState { - // - reload_on_change: bool, -} +// pub struct DevelopState { +// // +// reload_on_change: bool, +// } -pub async fn develop(options: DevelopOptions) -> Result<()> { - // - log::info!("Starting development server 🚀"); - let mut cfg = CrateConfig::new()?; - cfg.with_develop_options(&options); +// pub async fn develop(options: DevelopOptions) -> Result<()> { +// // +// log::info!("Starting development server 🚀"); +// let mut cfg = CrateConfig::new()?; +// cfg.with_develop_options(&options); - let out_dir = cfg.out_dir.clone(); +// let out_dir = cfg.out_dir.clone(); - let is_err = Arc::new(AtomicBool::new(false)); +// let is_err = Arc::new(AtomicBool::new(false)); - // Spawn the server onto a seperate task - // This lets the task progress while we handle file updates - let server = async_std::task::spawn(launch_server(out_dir, is_err.clone())); - let watcher = async_std::task::spawn(watch_directory(cfg.clone(), is_err.clone())); +// // Spawn the server onto a seperate task +// // This lets the task progress while we handle file updates +// let server = async_std::task::spawn(launch_server(out_dir, is_err.clone())); +// let watcher = async_std::task::spawn(watch_directory(cfg.clone(), is_err.clone())); - match server.race(watcher).await { - Err(e) => log::warn!("Error running development server, {:?}", e), - _ => {} - } +// match server.race(watcher).await { +// Err(e) => log::warn!("Error running development server, {:?}", e), +// _ => {} +// } - Ok(()) -} +// Ok(()) +// } -async fn watch_directory(config: CrateConfig, is_err: ErrStatus) -> Result<()> { - // Create a channel to receive the events. - let (watcher_tx, watcher_rx) = async_std::channel::bounded(100); +// async fn watch_directory(config: CrateConfig, is_err: ErrStatus) -> Result<()> { +// // Create a channel to receive the events. +// let (watcher_tx, watcher_rx) = async_std::channel::bounded(100); - // Automatically select the best implementation for your platform. - // You can also access each implementation directly e.g. INotifyWatcher. - let mut watcher: RecommendedWatcher = Watcher::new(move |res| { - async_std::task::block_on(watcher_tx.send(res)); - // send an event - let _ = async_std::task::block_on(watcher_tx.send(res)); - }) - .expect("failed to make watcher"); +// // Automatically select the best implementation for your platform. +// // You can also access each implementation directly e.g. INotifyWatcher. +// let mut watcher: RecommendedWatcher = Watcher::new(move |res| { +// async_std::task::block_on(watcher_tx.send(res)); +// // send an event +// let _ = async_std::task::block_on(watcher_tx.send(res)); +// }) +// .expect("failed to make watcher"); - let src_dir = crate::cargo::crate_root()?; +// let src_dir = crate::cargo::crate_root()?; - // Add a path to be watched. All files and directories at that path and - // below will be monitored for changes. - watcher - .watch(&src_dir.join("src"), RecursiveMode::Recursive) - .expect("Failed to watch dir"); +// // Add a path to be watched. All files and directories at that path and +// // below will be monitored for changes. +// watcher +// .watch(&src_dir.join("src"), RecursiveMode::Recursive) +// .expect("Failed to watch dir"); - match watcher.watch(&src_dir.join("examples"), RecursiveMode::Recursive) { - Ok(_) => {} - Err(e) => log::warn!("Failed to watch examples dir, {:?}", e), - } +// match watcher.watch(&src_dir.join("examples"), RecursiveMode::Recursive) { +// Ok(_) => {} +// Err(e) => log::warn!("Failed to watch examples dir, {:?}", e), +// } - 'run: loop { - match crate::builder::build(&config) { - Ok(_) => { - is_err.store(false, std::sync::atomic::Ordering::Relaxed); - async_std::task::sleep(std::time::Duration::from_millis(500)).await; - } - Err(err) => is_err.store(true, std::sync::atomic::Ordering::Relaxed), - }; +// 'run: loop { +// match crate::builder::build(&config) { +// Ok(_) => { +// is_err.store(false, std::sync::atomic::Ordering::Relaxed); +// async_std::task::sleep(std::time::Duration::from_millis(500)).await; +// } +// Err(err) => is_err.store(true, std::sync::atomic::Ordering::Relaxed), +// }; - let mut msg = None; - loop { - let new_msg = watcher_rx.recv().await.unwrap().unwrap(); - if !watcher_rx.is_empty() { - msg = Some(new_msg); - break; - } - } +// let mut msg = None; +// loop { +// let new_msg = watcher_rx.recv().await.unwrap().unwrap(); +// if !watcher_rx.is_empty() { +// msg = Some(new_msg); +// break; +// } +// } - info!("File updated, rebuilding app"); - } - Ok(()) -} +// info!("File updated, rebuilding app"); +// } +// Ok(()) +// } -async fn launch_server(outdir: PathBuf, is_err: ErrStatus) -> Result<()> { - let _crate_dir = crate::cargo::crate_root()?; - let _workspace_dir = crate::cargo::workspace_root()?; +// async fn launch_server(outdir: PathBuf, is_err: ErrStatus) -> Result<()> { +// let _crate_dir = crate::cargo::crate_root()?; +// let _workspace_dir = crate::cargo::workspace_root()?; - let mut app = tide::with_state(ServerState::new(outdir.to_owned(), is_err)); +// let mut app = tide::with_state(ServerState::new(outdir.to_owned(), is_err)); - let file_path = format!("{}/index.html", outdir.display()); - log::info!("Serving {}", file_path); - let p = outdir.display().to_string(); +// let file_path = format!("{}/index.html", outdir.display()); +// log::info!("Serving {}", file_path); +// let p = outdir.display().to_string(); - app.at("/") - .get(|req: tide::Request| async move { - log::info!("Connected to development server"); - let state = req.state(); +// app.at("/") +// .get(|req: tide::Request| async move { +// log::info!("Connected to development server"); +// let state = req.state(); - match state.is_err.load(std::sync::atomic::Ordering::Relaxed) { - true => { - // - let mut resp = - tide::Body::from_string(format!(include_str!("../err.html"), err = "_")); - resp.set_mime(HTML); +// match state.is_err.load(std::sync::atomic::Ordering::Relaxed) { +// true => { +// // +// let mut resp = +// tide::Body::from_string(format!(include_str!("../err.html"), err = "_")); +// resp.set_mime(HTML); - Ok(resp) - } - false => { - Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?) - } - } - }) - .serve_dir(p)?; - // .serve_file(file_path) - // .unwrap(); +// Ok(resp) +// } +// false => { +// Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?) +// } +// } +// }) +// .serve_dir(p)?; +// // .serve_file(file_path) +// // .unwrap(); - let port = "8080"; - let serve_addr = format!("127.0.0.1:{}", port); +// let port = "8080"; +// let serve_addr = format!("127.0.0.1:{}", port); - info!("App available at http://{}/", serve_addr); - app.listen(serve_addr).await?; - Ok(()) -} +// info!("App available at http://{}/", serve_addr); +// app.listen(serve_addr).await?; +// Ok(()) +// } -/// https://github.com/http-rs/tide/blob/main/examples/state.rs -/// Tide seems to prefer using state instead of injecting into the app closure -/// The app closure needs to be static and -#[derive(Clone)] -struct ServerState { - serv_path: PathBuf, - is_err: ErrStatus, -} +// /// https://github.com/http-rs/tide/blob/main/examples/state.rs +// /// Tide seems to prefer using state instead of injecting into the app closure +// /// The app closure needs to be static and +// #[derive(Clone)] +// struct ServerState { +// serv_path: PathBuf, +// is_err: ErrStatus, +// } -type ErrStatus = Arc; +// type ErrStatus = Arc; -impl ServerState { - fn new(serv_path: PathBuf, is_err: ErrStatus) -> Self { - Self { serv_path, is_err } - } -} +// impl ServerState { +// fn new(serv_path: PathBuf, is_err: ErrStatus) -> Self { +// Self { serv_path, is_err } +// } +// } diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 9695870c0..36c050bd3 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,9 +1,36 @@ -use crate::cfg::ConfigOptsBuild; +use crate::{cfg::ConfigOptsServe, server}; use anyhow::Result; use std::path::PathBuf; +use std::io::Write; use structopt::StructOpt; -/// Build the Rust WASM app and all of its assets. +mod develop; + +/// Run the WASM project on dev-server #[derive(Clone, Debug, StructOpt)] #[structopt(name = "serve")] -pub struct Serve {} +pub struct Serve { + #[structopt(flatten)] + pub serve: ConfigOptsServe, +} + +impl Serve { + pub async fn serve(self) -> anyhow::Result<()> { + + let mut crate_config = crate::CrateConfig::new()?; + // change the relase state. + crate_config.with_release(self.serve.release); + + crate::builder::build(&crate_config).expect("build failed"); + + let serve_html = String::from(include_str!("../../server/serve.html")); + + let mut file = std::fs::File::create(crate_config.out_dir.join("index.html"))?; + file.write_all(serve_html.as_bytes())?; + + // start the develop server + server::startup(crate_config.clone()).await?; + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index ea8393484..d4d3d7953 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod builder; +pub mod server; pub use builder::*; pub mod cargo; @@ -16,4 +17,4 @@ pub use error::*; pub mod logging; pub use logging::*; -pub mod watch; \ No newline at end of file +pub mod watch; diff --git a/src/main.rs b/src/main.rs index 2ec44eb97..a58794783 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,30 +3,28 @@ use structopt::StructOpt; #[tokio::main] async fn main() -> Result<()> { - let args = Cli::from_args(); set_up_logging(); match args.action { Commands::Translate(opts) => { opts.translate(); - } - + } + Commands::Build(opts) => { opts.build(); } - // Commands::Clean(_) => { - // // - // } + // Commands::Clean(_) => { + // // + // } - // Commands::Config(_) => { - // // - // } - - Commands::Serve(_) => { - // - } + // Commands::Config(_) => { + // // + // } + Commands::Serve(opts) => { + opts.serve().await; + } } Ok(()) diff --git a/src/server/mod.rs b/src/server/mod.rs new file mode 100644 index 000000000..2e937dbb8 --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,87 @@ +use axum::{ + extract::{ + ws::{Message, WebSocket}, + Extension, TypedHeader, WebSocketUpgrade, + }, + http::StatusCode, + response::IntoResponse, + routing::{get, get_service}, + AddExtensionLayer, Router, +}; +use notify::{watcher, Watcher, DebouncedEvent}; +use std::sync::{mpsc::channel, Arc, Mutex}; +use std::time::Duration; +use tower_http::services::ServeDir; + +use crate::{CrateConfig, builder}; + +struct WsRelodState { + update: bool, +} + +impl WsRelodState { fn change(&mut self) { self.update = !self.update } } + +pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { + + log::info!("Starting development server 🚀"); + + let (tx, rx) = channel(); + + let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); + watcher.watch(config.crate_dir.join("src").clone(), notify::RecursiveMode::Recursive).unwrap(); + + let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false })); + + let watcher_conf = config.clone(); + let watcher_ws_state = ws_reload_state.clone(); + tokio::spawn(async move { + loop { + if let Ok(v) = rx.recv() { + match v { + DebouncedEvent::Create(_) | DebouncedEvent::Write(_) | + DebouncedEvent::Remove(_) | DebouncedEvent::Rename(_, _) => { + builder::build(&watcher_conf).unwrap(); + watcher_ws_state.lock().unwrap().change(); + }, + _ => {} + } + } + } + }); + + let app = Router::new() + .route("/ws", get(ws_handler)) + .fallback( + get_service(ServeDir::new(config.out_dir)).handle_error( + |error: std::io::Error| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }, + ), + ) + .layer(AddExtensionLayer::new(ws_reload_state.clone())); + + let port = "8080"; + axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) + .serve(app.into_make_service()) + .await?; + + Ok(()) +} + +async fn ws_handler( + ws: WebSocketUpgrade, + _: Option>, + Extension(state): Extension>>, +) -> impl IntoResponse { + ws.on_upgrade(|mut socket| async move { + loop { + if state.lock().unwrap().update { + socket.send(Message::Text(String::from("reload"))).await.unwrap(); + state.lock().unwrap().change(); + } + } + }) +} diff --git a/src/server/serve.html b/src/server/serve.html new file mode 100644 index 000000000..98997403b --- /dev/null +++ b/src/server/serve.html @@ -0,0 +1,28 @@ + + + + + Dioxus-CLI Dev Server + + +
+ + + + + + + From d18a93f129132bd1ea59344fe757389eae603edc Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sat, 22 Jan 2022 22:19:59 +0800 Subject: [PATCH 022/516] fix: fixed warn info --- src/builder.rs | 24 +++++++++++------------- src/cli/build/mod.rs | 4 +--- src/cli/cfg.rs | 5 +---- src/cli/clean/mod.rs | 3 --- src/cli/mod.rs | 2 -- src/cli/serve/mod.rs | 2 -- src/cli/translate/to_component.rs | 1 + src/config.rs | 2 +- src/main.rs | 6 +++--- src/server/mod.rs | 11 ++++++----- src/server/serve.html | 27 +++++++++++++++++++-------- 11 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 89451e163..101d31ee5 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -3,7 +3,7 @@ use crate::{ error::{Error, Result}, }; use std::{ - io::{Read, Write}, + io::Write, process::Command, }; use wasm_bindgen_cli_support::Bindgen; @@ -29,14 +29,15 @@ pub fn build(config: &CrateConfig) -> Result<()> { let t_start = std::time::Instant::now(); // [1] Build the .wasm module - log::info!("Running build commands..."); + log::info!("🚅 Running build commands..."); let mut cmd = Command::new("cargo"); cmd.current_dir(&crate_dir) .arg("build") .arg("--target") .arg("wasm32-unknown-unknown") - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()); + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + ; if config.release { cmd.arg("--release"); @@ -48,16 +49,13 @@ pub fn build(config: &CrateConfig) -> Result<()> { ExecutableType::Example(name) => cmd.arg("--example").arg(name), }; - let mut child = cmd.spawn()?; + let output = cmd.output()?; - let output = child.wait()?; - - if output.success() { - log::info!("Build complete!"); + if output.status.success() { + std::io::stdout().write_all(&output.stdout).unwrap(); } else { log::error!("Build failed!"); - let mut reason = String::new(); - child.stderr.unwrap().read_to_string(&mut reason)?; + let reason = String::from_utf8_lossy(&output.stderr).to_string(); return Err(Error::BuildFailed(reason)); } @@ -98,8 +96,8 @@ pub fn build(config: &CrateConfig) -> Result<()> { // [5] Generate the html file with the module name // TODO: support names via options - log::info!("Writing to '{:#?}' directory...", out_dir); + // log::info!("Writing to '{:#?}' directory...", out_dir); let copy_options = fs_extra::dir::CopyOptions::new(); if static_dir.is_dir() { match fs_extra::dir::copy(static_dir, out_dir, ©_options) { @@ -111,7 +109,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { } let t_end = std::time::Instant::now(); - log::info!("Done in {}ms! 🎉", (t_end - t_start).as_millis()); + log::info!("🏁 Done in {}ms!", (t_end - t_start).as_millis()); Ok(()) } diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 58d797485..fc329a305 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,6 +1,4 @@ use crate::cfg::ConfigOptsBuild; -use anyhow::Result; -use std::path::PathBuf; use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. @@ -18,7 +16,7 @@ impl Build { // change the relase state. crate_config.with_release(self.build.release); - crate::builder::build(&crate_config); + crate::builder::build(&crate_config)?; Ok(()) } diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 5736ebc19..2c5f42a26 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -1,11 +1,8 @@ -use anyhow::Result; use std::path::PathBuf; use structopt::StructOpt; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use std::collections::HashMap; -use std::str::FromStr; -use std::sync::Arc; /// Config options for the build system. #[derive(Clone, Debug, Default, Deserialize, StructOpt)] diff --git a/src/cli/clean/mod.rs b/src/cli/clean/mod.rs index aeeedf523..1edcd053a 100644 --- a/src/cli/clean/mod.rs +++ b/src/cli/clean/mod.rs @@ -1,6 +1,3 @@ -use crate::cfg::ConfigOptsBuild; -use anyhow::Result; -use std::path::PathBuf; use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 32492459a..a739f9a01 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,5 +1,3 @@ -use anyhow::{Context, Result}; -use std::path::PathBuf; use structopt::StructOpt; pub mod build; diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 36c050bd3..f2ac2b69f 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,6 +1,4 @@ use crate::{cfg::ConfigOptsServe, server}; -use anyhow::Result; -use std::path::PathBuf; use std::io::Write; use structopt::StructOpt; diff --git a/src/cli/translate/to_component.rs b/src/cli/translate/to_component.rs index b08161e9c..d64854d37 100644 --- a/src/cli/translate/to_component.rs +++ b/src/cli/translate/to_component.rs @@ -20,6 +20,7 @@ pub fn convert_html_to_component(html: &str) -> Result { }) } +#[allow(dead_code)] pub struct ComponentRenderer { dom: Dom, icon_index: usize, diff --git a/src/config.rs b/src/config.rs index 47920c583..d17294b18 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,5 @@ use crate::error::Result; -use std::{io::Write, path::PathBuf, process::Command}; +use std::path::PathBuf; #[derive(Debug, Clone)] pub struct CrateConfig { diff --git a/src/main.rs b/src/main.rs index a58794783..41fa5835b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,11 @@ async fn main() -> Result<()> { match args.action { Commands::Translate(opts) => { - opts.translate(); + opts.translate()?; } Commands::Build(opts) => { - opts.build(); + opts.build()?; } // Commands::Clean(_) => { @@ -23,7 +23,7 @@ async fn main() -> Result<()> { // // // } Commands::Serve(opts) => { - opts.serve().await; + opts.serve().await?; } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 2e937dbb8..6c6c08c1f 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,6 +1,6 @@ use axum::{ extract::{ - ws::{Message, WebSocket}, + ws::Message, Extension, TypedHeader, WebSocketUpgrade, }, http::StatusCode, @@ -19,11 +19,11 @@ struct WsRelodState { update: bool, } -impl WsRelodState { fn change(&mut self) { self.update = !self.update } } +impl WsRelodState { fn change(&mut self) { self.update = !self.update; } } pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { - log::info!("Starting development server 🚀"); + log::info!("🚀 Starting development server..."); let (tx, rx) = channel(); @@ -40,8 +40,9 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { match v { DebouncedEvent::Create(_) | DebouncedEvent::Write(_) | DebouncedEvent::Remove(_) | DebouncedEvent::Rename(_, _) => { - builder::build(&watcher_conf).unwrap(); - watcher_ws_state.lock().unwrap().change(); + if let Ok(_) = builder::build(&watcher_conf) { + watcher_ws_state.lock().unwrap().change(); + } }, _ => {} } diff --git a/src/server/serve.html b/src/server/serve.html index 98997403b..b5268bd68 100644 --- a/src/server/serve.html +++ b/src/server/serve.html @@ -14,15 +14,26 @@ From 0e2c05be66fc994ad8ac2e03b34c0e48632c3296 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 23 Jan 2022 01:29:36 +0800 Subject: [PATCH 023/516] feat: add `init` command --- README.md | 5 +- src/builder.rs | 8 +- src/cli/init/mod.rs | 61 +++++++++++++ src/cli/mod.rs | 8 +- src/cli/serve/develop.rs | 145 ------------------------------ src/cli/serve/events.rs | 0 src/cli/serve/mod.rs | 3 - src/cli/translate/to_component.rs | 1 + src/lib.rs | 4 +- src/main.rs | 18 +++- src/{ => server}/err.html | 0 src/server/mod.rs | 64 +++++++------ src/template/config.toml | 9 ++ src/template/default.rs | 16 ++++ src/watch.rs | 1 - 15 files changed, 151 insertions(+), 192 deletions(-) create mode 100644 src/cli/init/mod.rs delete mode 100644 src/cli/serve/develop.rs delete mode 100644 src/cli/serve/events.rs rename src/{ => server}/err.html (100%) create mode 100644 src/template/config.toml create mode 100644 src/template/default.rs delete mode 100644 src/watch.rs diff --git a/README.md b/README.md index 34be58eda..75e05b3cf 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ # 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. +dioxus-cli (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 @@ -19,5 +19,4 @@ dioxus-studio (inspired by wasm-pack and webpack) is a tool to help get dioxus p $ cargo install dioxus-cli ``` -Now, `dioxus` is in your path. - +Now, `dioxus` is in your path. \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 101d31ee5..24d9c3151 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2,10 +2,7 @@ use crate::{ config::{CrateConfig, ExecutableType}, error::{Error, Result}, }; -use std::{ - io::Write, - process::Command, -}; +use std::{io::Write, process::Command}; use wasm_bindgen_cli_support::Bindgen; pub fn build(config: &CrateConfig) -> Result<()> { @@ -36,8 +33,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { .arg("--target") .arg("wasm32-unknown-unknown") .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()) - ; + .stderr(std::process::Stdio::inherit()); if config.release { cmd.arg("--release"); diff --git a/src/cli/init/mod.rs b/src/cli/init/mod.rs new file mode 100644 index 000000000..2325d9c31 --- /dev/null +++ b/src/cli/init/mod.rs @@ -0,0 +1,61 @@ +use std::{process::{Command, Stdio}, fs::File, path::PathBuf, io::Write}; + +use structopt::StructOpt; + +use crate::{error::{Error, Result}, cargo}; + +/// Build the Rust WASM app and all of its assets. +#[derive(Clone, Debug, StructOpt)] +#[structopt(name = "init")] +pub struct Init { + + /// Init project path + #[structopt(default_value = ".")] + path: String, + + /// Template path + #[structopt(default_value = "default", long)] + template: String, + +} + +impl Init { + pub fn init(self) -> Result<()> { + + log::info!("🔧 Start to init a new project '{}'.", self.path); + + let project_path = PathBuf::from(&self.path); + + let output = Command::new("cargo") + .arg("init") + .arg(&self.path) + .arg("--bin") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output()? + ; + + if !output.status.success() { + return Err(Error::CargoError("Cargo init failed".into())); + } + + // get the template code + let template_str = match self.template { + _ => include_str!("../../template/default.rs"), + }; + + let main_rs_file = project_path.join("src").join("main.rs"); + if !main_rs_file.is_file() { + return Err(Error::FailedToWrite); + } + + let mut file = File::create(main_rs_file)?; + file.write_all(&template_str.as_bytes())?; + + // log::info!("🎯 Project initialization completed."); + + + + Ok(()) + } +} \ No newline at end of file diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a739f9a01..ddc55d5c4 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -5,6 +5,7 @@ pub mod cfg; pub mod clean; pub mod serve; pub mod translate; +pub mod init; /// Build, bundle, & ship your Dioxus app. /// @@ -27,12 +28,15 @@ pub struct Cli { #[derive(StructOpt)] pub enum Commands { - // /// Build the Rust WASM app and all of its assets. + /// 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. + /// Build, watch & serve the Rust WASM app and all of its assets. Serve(serve::Serve), + /// Init a new project for Dioxus. + Init(init::Init), + // /// Clean output artifacts. // Clean(clean::Clean), diff --git a/src/cli/serve/develop.rs b/src/cli/serve/develop.rs deleted file mode 100644 index 27aba99ee..000000000 --- a/src/cli/serve/develop.rs +++ /dev/null @@ -1,145 +0,0 @@ -// use crate::{cli::DevelopOptions, config::CrateConfig, error::Result}; -// use async_std::prelude::FutureExt; - -// use log::info; -// use notify::{RecommendedWatcher, RecursiveMode, Watcher}; -// use std::path::PathBuf; -// use std::sync::atomic::AtomicBool; -// use std::sync::Arc; -// use tide::http::mime::HTML; -// use tide::http::Mime; - -// pub struct DevelopState { -// // -// reload_on_change: bool, -// } - -// pub async fn develop(options: DevelopOptions) -> Result<()> { -// // -// log::info!("Starting development server 🚀"); -// let mut cfg = CrateConfig::new()?; -// cfg.with_develop_options(&options); - -// let out_dir = cfg.out_dir.clone(); - -// let is_err = Arc::new(AtomicBool::new(false)); - -// // Spawn the server onto a seperate task -// // This lets the task progress while we handle file updates -// let server = async_std::task::spawn(launch_server(out_dir, is_err.clone())); -// let watcher = async_std::task::spawn(watch_directory(cfg.clone(), is_err.clone())); - -// match server.race(watcher).await { -// Err(e) => log::warn!("Error running development server, {:?}", e), -// _ => {} -// } - -// Ok(()) -// } - -// async fn watch_directory(config: CrateConfig, is_err: ErrStatus) -> Result<()> { -// // Create a channel to receive the events. -// let (watcher_tx, watcher_rx) = async_std::channel::bounded(100); - -// // Automatically select the best implementation for your platform. -// // You can also access each implementation directly e.g. INotifyWatcher. -// let mut watcher: RecommendedWatcher = Watcher::new(move |res| { -// async_std::task::block_on(watcher_tx.send(res)); -// // send an event -// let _ = async_std::task::block_on(watcher_tx.send(res)); -// }) -// .expect("failed to make watcher"); - -// let src_dir = crate::cargo::crate_root()?; - -// // Add a path to be watched. All files and directories at that path and -// // below will be monitored for changes. -// watcher -// .watch(&src_dir.join("src"), RecursiveMode::Recursive) -// .expect("Failed to watch dir"); - -// match watcher.watch(&src_dir.join("examples"), RecursiveMode::Recursive) { -// Ok(_) => {} -// Err(e) => log::warn!("Failed to watch examples dir, {:?}", e), -// } - -// 'run: loop { -// match crate::builder::build(&config) { -// Ok(_) => { -// is_err.store(false, std::sync::atomic::Ordering::Relaxed); -// async_std::task::sleep(std::time::Duration::from_millis(500)).await; -// } -// Err(err) => is_err.store(true, std::sync::atomic::Ordering::Relaxed), -// }; - -// let mut msg = None; -// loop { -// let new_msg = watcher_rx.recv().await.unwrap().unwrap(); -// if !watcher_rx.is_empty() { -// msg = Some(new_msg); -// break; -// } -// } - -// info!("File updated, rebuilding app"); -// } -// Ok(()) -// } - -// async fn launch_server(outdir: PathBuf, is_err: ErrStatus) -> Result<()> { -// let _crate_dir = crate::cargo::crate_root()?; -// let _workspace_dir = crate::cargo::workspace_root()?; - -// let mut app = tide::with_state(ServerState::new(outdir.to_owned(), is_err)); - -// let file_path = format!("{}/index.html", outdir.display()); -// log::info!("Serving {}", file_path); -// let p = outdir.display().to_string(); - -// app.at("/") -// .get(|req: tide::Request| async move { -// log::info!("Connected to development server"); -// let state = req.state(); - -// match state.is_err.load(std::sync::atomic::Ordering::Relaxed) { -// true => { -// // -// let mut resp = -// tide::Body::from_string(format!(include_str!("../err.html"), err = "_")); -// resp.set_mime(HTML); - -// Ok(resp) -// } -// false => { -// Ok(tide::Body::from_file(state.serv_path.clone().join("index.html")).await?) -// } -// } -// }) -// .serve_dir(p)?; -// // .serve_file(file_path) -// // .unwrap(); - -// let port = "8080"; -// let serve_addr = format!("127.0.0.1:{}", port); - -// info!("App available at http://{}/", serve_addr); -// app.listen(serve_addr).await?; -// Ok(()) -// } - -// /// https://github.com/http-rs/tide/blob/main/examples/state.rs -// /// Tide seems to prefer using state instead of injecting into the app closure -// /// The app closure needs to be static and -// #[derive(Clone)] -// struct ServerState { -// serv_path: PathBuf, -// is_err: ErrStatus, -// } - -// type ErrStatus = Arc; - -// impl ServerState { -// fn new(serv_path: PathBuf, is_err: ErrStatus) -> Self { -// Self { serv_path, is_err } -// } -// } diff --git a/src/cli/serve/events.rs b/src/cli/serve/events.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index f2ac2b69f..d6287f07d 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -2,8 +2,6 @@ use crate::{cfg::ConfigOptsServe, server}; use std::io::Write; use structopt::StructOpt; -mod develop; - /// Run the WASM project on dev-server #[derive(Clone, Debug, StructOpt)] #[structopt(name = "serve")] @@ -14,7 +12,6 @@ pub struct Serve { impl Serve { pub async fn serve(self) -> anyhow::Result<()> { - let mut crate_config = crate::CrateConfig::new()?; // change the relase state. crate_config.with_release(self.serve.release); diff --git a/src/cli/translate/to_component.rs b/src/cli/translate/to_component.rs index d64854d37..972da7a25 100644 --- a/src/cli/translate/to_component.rs +++ b/src/cli/translate/to_component.rs @@ -4,6 +4,7 @@ //! - [ ] Extracts svgs //! - [ ] Attempts to extract lists +#[allow(unused_imports)] use std::{ fmt::{Display, Formatter}, io::Write, diff --git a/src/lib.rs b/src/lib.rs index d4d3d7953..15c10a3b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,4 @@ pub mod error; pub use error::*; pub mod logging; -pub use logging::*; - -pub mod watch; +pub use logging::*; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 41fa5835b..69636c6e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,15 @@ async fn main() -> Result<()> { match args.action { Commands::Translate(opts) => { - opts.translate()?; + if let Err(e) = opts.translate() { + log::error!("translate error: {}", e); + } } Commands::Build(opts) => { - opts.build()?; + if let Err(e) = opts.build() { + log::error!("build error: {}", e); + } } // Commands::Clean(_) => { @@ -23,7 +27,15 @@ async fn main() -> Result<()> { // // // } Commands::Serve(opts) => { - opts.serve().await?; + if let Err(e) = opts.serve().await { + log::error!("serve error: {}", e); + } + } + + Commands::Init(opts) => { + if let Err(e) = opts.init() { + log::error!("init error: {}", e); + } } } diff --git a/src/err.html b/src/server/err.html similarity index 100% rename from src/err.html rename to src/server/err.html diff --git a/src/server/mod.rs b/src/server/mod.rs index 6c6c08c1f..7c0713c8d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,34 +1,40 @@ use axum::{ - extract::{ - ws::Message, - Extension, TypedHeader, WebSocketUpgrade, - }, + extract::{ws::Message, Extension, TypedHeader, WebSocketUpgrade}, http::StatusCode, response::IntoResponse, routing::{get, get_service}, AddExtensionLayer, Router, }; -use notify::{watcher, Watcher, DebouncedEvent}; +use notify::{watcher, DebouncedEvent, Watcher}; use std::sync::{mpsc::channel, Arc, Mutex}; use std::time::Duration; use tower_http::services::ServeDir; -use crate::{CrateConfig, builder}; +use crate::{builder, CrateConfig}; struct WsRelodState { update: bool, } -impl WsRelodState { fn change(&mut self) { self.update = !self.update; } } +impl WsRelodState { + fn change(&mut self) { + self.update = !self.update; + } +} pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { - log::info!("🚀 Starting development server..."); - + let (tx, rx) = channel(); + // file watcher: check file change let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); - watcher.watch(config.crate_dir.join("src").clone(), notify::RecursiveMode::Recursive).unwrap(); + watcher + .watch( + config.crate_dir.join("src").clone(), + notify::RecursiveMode::Recursive, + ) + .unwrap(); let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false })); @@ -38,37 +44,40 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { loop { if let Ok(v) = rx.recv() { match v { - DebouncedEvent::Create(_) | DebouncedEvent::Write(_) | - DebouncedEvent::Remove(_) | DebouncedEvent::Rename(_, _) => { + DebouncedEvent::Create(_) + | DebouncedEvent::Write(_) + | DebouncedEvent::Remove(_) + | DebouncedEvent::Rename(_, _) => { 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(); } - }, + } _ => {} } } - } + } }); let app = Router::new() .route("/ws", get(ws_handler)) - .fallback( - get_service(ServeDir::new(config.out_dir)).handle_error( - |error: std::io::Error| async move { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), - ) - }, - ), - ) + .fallback(get_service(ServeDir::new(config.out_dir)).handle_error( + |error: std::io::Error| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }, + )) .layer(AddExtensionLayer::new(ws_reload_state.clone())); + // start serve dev-server at 0.0.0.0:8080 let port = "8080"; + log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port); axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) .serve(app.into_make_service()) .await?; - Ok(()) } @@ -80,7 +89,10 @@ async fn ws_handler( ws.on_upgrade(|mut socket| async move { loop { if state.lock().unwrap().update { - socket.send(Message::Text(String::from("reload"))).await.unwrap(); + socket + .send(Message::Text(String::from("reload"))) + .await + .unwrap(); state.lock().unwrap().change(); } } diff --git a/src/template/config.toml b/src/template/config.toml new file mode 100644 index 000000000..4c6c2be9c --- /dev/null +++ b/src/template/config.toml @@ -0,0 +1,9 @@ +[package] +name = "{project}" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { version = "0.1.7", features = ["web"] } diff --git a/src/template/default.rs b/src/template/default.rs new file mode 100644 index 000000000..c0fa412ea --- /dev/null +++ b/src/template/default.rs @@ -0,0 +1,16 @@ +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! ( + div { + style: "text-align: center;", + h2 { "🌗🚀 Dioxus" } + p { strong { "Frontend that scales." } } + p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." } + } + )) +} diff --git a/src/watch.rs b/src/watch.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/watch.rs +++ /dev/null @@ -1 +0,0 @@ - From 5395e7693c05caf5446a1cc6671de7e870366569 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 23 Jan 2022 10:54:24 +0800 Subject: [PATCH 024/516] fix: `dist` setting --- src/builder.rs | 9 +++++---- src/cli/build/mod.rs | 7 ++++++- src/cli/init/mod.rs | 38 +++++++++++++++++++++++++++++--------- src/cli/mod.rs | 3 +-- src/cli/serve/mod.rs | 11 +++++++---- src/lib.rs | 2 +- src/server/mod.rs | 13 ++++++++++--- src/template/config.toml | 2 +- src/template/default.rs | 2 +- 9 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 24d9c3151..aad275ff2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2,10 +2,10 @@ use crate::{ config::{CrateConfig, ExecutableType}, error::{Error, Result}, }; -use std::{io::Write, process::Command}; +use std::{io::Write, path::PathBuf, process::Command}; use wasm_bindgen_cli_support::Bindgen; -pub fn build(config: &CrateConfig) -> Result<()> { +pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { /* [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 @@ -15,7 +15,6 @@ pub fn build(config: &CrateConfig) -> Result<()> { */ let CrateConfig { - out_dir, crate_dir, target_dir, static_dir, @@ -23,10 +22,12 @@ pub fn build(config: &CrateConfig) -> Result<()> { .. } = config; + let out_dir = crate_dir.join(dist); + let t_start = std::time::Instant::now(); // [1] Build the .wasm module - log::info!("🚅 Running build commands..."); + log::info!("🚅 Running build command..."); let mut cmd = Command::new("cargo"); cmd.current_dir(&crate_dir) .arg("build") diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index fc329a305..760c2fd8e 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use crate::cfg::ConfigOptsBuild; use structopt::StructOpt; @@ -16,7 +18,10 @@ impl Build { // change the relase state. crate_config.with_release(self.build.release); - crate::builder::build(&crate_config)?; + crate::builder::build( + &crate_config, + self.build.dist.unwrap_or(PathBuf::from("dist")), + )?; Ok(()) } diff --git a/src/cli/init/mod.rs b/src/cli/init/mod.rs index 2325d9c31..dde0ccca3 100644 --- a/src/cli/init/mod.rs +++ b/src/cli/init/mod.rs @@ -1,14 +1,18 @@ -use std::{process::{Command, Stdio}, fs::File, path::PathBuf, io::Write}; +use std::{ + fs::{File, OpenOptions}, + io::Write, + path::PathBuf, + process::{Command, Stdio}, +}; use structopt::StructOpt; -use crate::{error::{Error, Result}, cargo}; +use crate::error::{Error, Result}; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, StructOpt)] #[structopt(name = "init")] pub struct Init { - /// Init project path #[structopt(default_value = ".")] path: String, @@ -16,24 +20,26 @@ pub struct Init { /// Template path #[structopt(default_value = "default", long)] template: String, - } impl Init { pub fn init(self) -> Result<()> { - log::info!("🔧 Start to init a new project '{}'.", self.path); let project_path = PathBuf::from(&self.path); + if project_path.join("Cargo.toml").is_file() { + log::warn!("Path '{}' is initialized.", self.path); + return Ok(()); + } + let output = Command::new("cargo") .arg("init") .arg(&self.path) .arg("--bin") .stdout(Stdio::piped()) .stderr(Stdio::piped()) - .output()? - ; + .output()?; if !output.status.success() { return Err(Error::CargoError("Cargo init failed".into())); @@ -54,8 +60,22 @@ impl Init { // log::info!("🎯 Project initialization completed."); - + if !Command::new("cargo") + .args(["add", "dioxus", "--features web"]) + .output()? + .status + .success() + { + let mut file = OpenOptions::new() + .append(true) + .open(project_path.join("Cargo.toml"))?; + file.write_all("dioxus = { version = \"0.1.7\", features = [\"web\"] }".as_bytes())?; + } + + log::info!("\n💡 Project initialized:"); + log::info!("🎯> cd {}", self.path); + log::info!("🎯> dioxus serve"); Ok(()) } -} \ No newline at end of file +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index ddc55d5c4..93cbfef28 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -3,9 +3,9 @@ use structopt::StructOpt; pub mod build; pub mod cfg; pub mod clean; +pub mod init; pub mod serve; pub mod translate; -pub mod init; /// Build, bundle, & ship your Dioxus app. /// @@ -36,7 +36,6 @@ pub enum Commands { Serve(serve::Serve), /// Init a new project for Dioxus. Init(init::Init), - // /// Clean output artifacts. // Clean(clean::Clean), diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index d6287f07d..586d49b7c 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,5 +1,5 @@ use crate::{cfg::ConfigOptsServe, server}; -use std::io::Write; +use std::{io::Write, path::PathBuf}; use structopt::StructOpt; /// Run the WASM project on dev-server @@ -16,15 +16,18 @@ impl Serve { // change the relase state. crate_config.with_release(self.serve.release); - crate::builder::build(&crate_config).expect("build failed"); + let dist_path = self.serve.dist.clone().unwrap_or(PathBuf::from("dist")); + + crate::builder::build(&crate_config, dist_path.clone()).expect("build failed"); let serve_html = String::from(include_str!("../../server/serve.html")); - let mut file = std::fs::File::create(crate_config.out_dir.join("index.html"))?; + let mut file = + std::fs::File::create(crate_config.crate_dir.join(dist_path).join("index.html"))?; file.write_all(serve_html.as_bytes())?; // start the develop server - server::startup(crate_config.clone()).await?; + server::startup(crate_config.clone(), &self.serve).await?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 15c10a3b3..395cbc8dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,4 +15,4 @@ pub mod error; pub use error::*; pub mod logging; -pub use logging::*; \ No newline at end of file +pub use logging::*; diff --git a/src/server/mod.rs b/src/server/mod.rs index 7c0713c8d..4f04d3268 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,8 +6,11 @@ use axum::{ AddExtensionLayer, Router, }; use notify::{watcher, DebouncedEvent, Watcher}; -use std::sync::{mpsc::channel, Arc, Mutex}; use std::time::Duration; +use std::{ + path::PathBuf, + sync::{mpsc::channel, Arc, Mutex}, +}; use tower_http::services::ServeDir; use crate::{builder, CrateConfig}; @@ -22,7 +25,10 @@ impl WsRelodState { } } -pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { +pub async fn startup( + config: CrateConfig, + opts: &crate::cfg::ConfigOptsServe, +) -> anyhow::Result<()> { log::info!("🚀 Starting development server..."); let (tx, rx) = channel(); @@ -40,6 +46,7 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { let watcher_conf = config.clone(); let watcher_ws_state = ws_reload_state.clone(); + let dist_path = opts.dist.clone().unwrap_or(PathBuf::from("dist")); tokio::spawn(async move { loop { if let Ok(v) = rx.recv() { @@ -48,7 +55,7 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { | DebouncedEvent::Write(_) | DebouncedEvent::Remove(_) | DebouncedEvent::Rename(_, _) => { - if let Ok(_) = builder::build(&watcher_conf) { + if let Ok(_) = builder::build(&watcher_conf, dist_path.clone()) { // change the websocket reload state to true; // the page will auto-reload. watcher_ws_state.lock().unwrap().change(); diff --git a/src/template/config.toml b/src/template/config.toml index 4c6c2be9c..9644babc1 100644 --- a/src/template/config.toml +++ b/src/template/config.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus = { version = "0.1.7", features = ["web"] } +dioxus = { version = "0.1.7", features = ["web"] } \ No newline at end of file diff --git a/src/template/default.rs b/src/template/default.rs index c0fa412ea..ac1614fc4 100644 --- a/src/template/default.rs +++ b/src/template/default.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; fn main() { - dioxus::desktop::launch(app); + dioxus::web::launch(app); } fn app(cx: Scope) -> Element { From 70adff7e443500e05ce8ff32a803971c4f816aff Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 23 Jan 2022 12:11:17 +0800 Subject: [PATCH 025/516] feat: add `dioxus.toml` support --- Cargo.toml | 1 + Dioxus.toml | 42 ++++++++++++++++++++++++++++++++++++++++++ src/builder.rs | 13 ++----------- src/config.rs | 40 +++++++++++++++++++++++++++++++++++++++- src/server/mod.rs | 24 ++++++++++++++---------- 5 files changed, 98 insertions(+), 22 deletions(-) create mode 100644 Dioxus.toml diff --git a/Cargo.toml b/Cargo.toml index cd1dec98b..8bb6fd3ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ wasm-bindgen-cli-support = "0.2.79" anyhow = "1.0.38" serde = { version = "1.0.133", features = ["derive"] } serde_json = "1" +toml = "0.5.8" fs_extra = "1.2.0" cargo_toml = "0.10.0" futures = "0.3.12" diff --git a/Dioxus.toml b/Dioxus.toml new file mode 100644 index 000000000..abd2d1ee4 --- /dev/null +++ b/Dioxus.toml @@ -0,0 +1,42 @@ +[application] + +# App (Project) Name +name = "dioxus-cli" + +# Dioxus App Support Platform +# desktop, web, mobile, ssr +platforms = ["web"] + +[web.app] + +# 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 = [ + "./my-style.css" +] + +# Javascript code file +script = [ + "./app.js" +] + +[web.resource.dev] + +# Javascript code file +# serve: [dev-server] only +script = [ + "./debug.js" +] \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index aad275ff2..58ee085fc 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -48,14 +48,12 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { let output = cmd.output()?; - if output.status.success() { - std::io::stdout().write_all(&output.stdout).unwrap(); - } else { + 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 let bindgen_outdir = out_dir.join("assets"); @@ -88,13 +86,6 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { .out_name("module") .generate(&bindgen_outdir)?; - // [4] - // TODO: wasm-opt - - // [5] Generate the html file with the module name - // TODO: support names via options - - // log::info!("Writing to '{:#?}' directory...", out_dir); let copy_options = fs_extra::dir::CopyOptions::new(); if static_dir.is_dir() { match fs_extra::dir::copy(static_dir, out_dir, ©_options) { diff --git a/src/config.rs b/src/config.rs index d17294b18..9d38160f2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,44 @@ use crate::error::Result; +use serde::{Deserialize, Serialize}; use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DioxusConfig { + application: ApplicationConfig, + web: WebConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ApplicationConfig { + name: String, + platform: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebConfig { + app: WebAppConfing, + watcher: WebWatcherConfing, + resource: WebResourceConfing, +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebAppConfing { + +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebWatcherConfing { + +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebResourceConfing { + +} + + #[derive(Debug, Clone)] pub struct CrateConfig { pub out_dir: PathBuf, @@ -86,4 +124,4 @@ impl CrateConfig { // self.release = options.release; // self.out_dir = tempfile::Builder::new().tempdir().expect("").into_path(); // } -} +} \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index 4f04d3268..6b91a2e71 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -33,6 +33,8 @@ pub async fn startup( let (tx, rx) = channel(); + let dist_path = opts.dist.clone().unwrap_or(PathBuf::from("dist")); + // file watcher: check file change let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); watcher @@ -46,7 +48,7 @@ pub async fn startup( let watcher_conf = config.clone(); let watcher_ws_state = ws_reload_state.clone(); - let dist_path = opts.dist.clone().unwrap_or(PathBuf::from("dist")); + let watcher_dist_path = dist_path.clone(); tokio::spawn(async move { loop { if let Ok(v) = rx.recv() { @@ -55,7 +57,7 @@ pub async fn startup( | DebouncedEvent::Write(_) | DebouncedEvent::Remove(_) | DebouncedEvent::Rename(_, _) => { - if let Ok(_) = builder::build(&watcher_conf, dist_path.clone()) { + if let Ok(_) = builder::build(&watcher_conf, watcher_dist_path.clone()) { // change the websocket reload state to true; // the page will auto-reload. watcher_ws_state.lock().unwrap().change(); @@ -69,14 +71,16 @@ pub async fn startup( let app = Router::new() .route("/ws", get(ws_handler)) - .fallback(get_service(ServeDir::new(config.out_dir)).handle_error( - |error: std::io::Error| async move { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), - ) - }, - )) + .fallback( + get_service(ServeDir::new(config.crate_dir.join(&dist_path))).handle_error( + |error: std::io::Error| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }, + ), + ) .layer(AddExtensionLayer::new(ws_reload_state.clone())); // start serve dev-server at 0.0.0.0:8080 From 9bb10c65a3cbabef127f6af5c0d1eadab1d01531 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 23 Jan 2022 17:35:59 +0800 Subject: [PATCH 026/516] feat: add `Dioxus.toml` --- src/assets/autoreload.js | 25 +++++++++++++ src/assets/index.html | 18 +++++++++ src/builder.rs | 81 ++++++++++++++++++++++++++-------------- src/cli/build/mod.rs | 7 +--- src/cli/cfg.rs | 16 -------- src/cli/init/mod.rs | 27 ++++++++++---- src/cli/serve/mod.rs | 28 ++++++++++---- src/config.rs | 78 ++++++++++++++++++++++++++++++-------- src/server/err.html | 19 ---------- src/server/mod.rs | 54 ++++++++++++++++----------- src/server/serve.html | 39 ------------------- src/template/config.toml | 43 +++++++++++++++++---- src/template/default.rs | 4 +- 13 files changed, 269 insertions(+), 170 deletions(-) create mode 100644 src/assets/autoreload.js create mode 100644 src/assets/index.html delete mode 100644 src/server/err.html delete mode 100644 src/server/serve.html diff --git a/src/assets/autoreload.js b/src/assets/autoreload.js new file mode 100644 index 000000000..ff4444085 --- /dev/null +++ b/src/assets/autoreload.js @@ -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; +})(); \ No newline at end of file diff --git a/src/assets/index.html b/src/assets/index.html new file mode 100644 index 000000000..12e1e5678 --- /dev/null +++ b/src/assets/index.html @@ -0,0 +1,18 @@ + + + {app_title} + + + {style_include} + + + +
+ + + {script_include} + + diff --git a/src/builder.rs b/src/builder.rs index 58ee085fc..381ef8b28 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,11 +1,12 @@ use crate::{ config::{CrateConfig, ExecutableType}, error::{Error, Result}, + DioxusConfig, }; -use std::{io::Write, path::PathBuf, process::Command}; +use std::process::Command; 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?) [2] Generate the appropriate build folders @@ -15,15 +16,15 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { */ let CrateConfig { + out_dir, crate_dir, target_dir, static_dir, executable, + dioxus_config, .. } = config; - let out_dir = crate_dir.join(dist); - let t_start = std::time::Instant::now(); // [1] Build the .wasm module @@ -53,7 +54,7 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { let reason = String::from_utf8_lossy(&output.stderr).to_string(); return Err(Error::BuildFailed(reason)); } - + // [2] Establish the output directory structure let bindgen_outdir = out_dir.join("assets"); @@ -83,7 +84,7 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { .keep_debug(true) .remove_name_section(false) .remove_producers_section(false) - .out_name("module") + .out_name(&dioxus_config.application.name) .generate(&bindgen_outdir)?; let copy_options = fs_extra::dir::CopyOptions::new(); @@ -101,28 +102,52 @@ pub fn build(config: &CrateConfig, dist: PathBuf) -> Result<()> { Ok(()) } -pub fn gen_page(module: &str) -> String { - format!( - r#" - - - - - - -
-
- - -
- - -"#, - module - ) +pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { + let mut html = String::from(include_str!("./assets/index.html")); + + let resouces = config.web.resource.clone(); + + let mut style_list = resouces.style.unwrap_or(vec![]); + let mut script_list = resouces.script.unwrap_or(vec![]); + + if serve { + let mut dev_style = resouces.dev.style.clone().unwrap_or(vec![]); + let mut dev_script = resouces.dev.script.clone().unwrap_or(vec![]); + style_list.append(&mut dev_style); + script_list.append(&mut dev_script); + } + + let mut style_str = String::new(); + for style in style_list { + style_str.push_str(&format!( + "\n", + &style.to_str().unwrap(), + )) + } + html = html.replace("{style_include}", &style_str); + + let mut script_str = String::new(); + for script in script_list { + script_str.push_str(&format!( + "\n", + &script.to_str().unwrap(), + )) + } + + html = html.replace("{script_include}", &script_str); + + if serve { + html += &format!( + "", + 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}; diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 760c2fd8e..fc329a305 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use crate::cfg::ConfigOptsBuild; use structopt::StructOpt; @@ -18,10 +16,7 @@ impl Build { // change the relase state. crate_config.with_release(self.build.release); - crate::builder::build( - &crate_config, - self.build.dist.unwrap_or(PathBuf::from("dist")), - )?; + crate::builder::build(&crate_config)?; Ok(()) } diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 2c5f42a26..ab68ce4f9 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -16,14 +16,6 @@ pub struct ConfigOptsBuild { #[serde(default)] pub release: bool, - /// The output dir for all final assets [default: dist] - #[structopt(short, long, parse(from_os_str))] - pub dist: Option, - - /// The public URL from which assets are to be served [default: /] - #[structopt(long, parse(from_str=parse_public_url))] - pub public_url: Option, - /// Optional pattern for the app loader script [default: None] /// /// Patterns should include the sequences `{base}`, `{wasm}`, and `{js}` in order to @@ -74,14 +66,6 @@ pub struct ConfigOptsServe { #[structopt(long)] #[serde(default)] pub release: bool, - - /// The output dir for all final assets [default: dist] - #[structopt(short, long, parse(from_os_str))] - pub dist: Option, - - /// The public URL from which assets are to be served [default: /] - #[structopt(long, parse(from_str=parse_public_url))] - pub public_url: Option, } /// Ensure the given value for `--public-url` is formatted correctly. diff --git a/src/cli/init/mod.rs b/src/cli/init/mod.rs index dde0ccca3..52dcf4c14 100644 --- a/src/cli/init/mod.rs +++ b/src/cli/init/mod.rs @@ -13,9 +13,9 @@ use crate::error::{Error, Result}; #[derive(Clone, Debug, StructOpt)] #[structopt(name = "init")] pub struct Init { - /// Init project path + /// Init project name #[structopt(default_value = ".")] - path: String, + name: String, /// Template path #[structopt(default_value = "default", long)] @@ -24,18 +24,23 @@ pub struct Init { impl Init { 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() { - log::warn!("Path '{}' is initialized.", self.path); + log::warn!("Folder '{}' is initialized.", self.name); return Ok(()); } let output = Command::new("cargo") .arg("init") - .arg(&self.path) + .arg(&format!("./{}", self.name)) .arg("--bin") .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -58,6 +63,11 @@ impl Init { let mut file = File::create(main_rs_file)?; 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."); if !Command::new("cargo") @@ -72,8 +82,9 @@ impl Init { file.write_all("dioxus = { version = \"0.1.7\", features = [\"web\"] }".as_bytes())?; } - log::info!("\n💡 Project initialized:"); - log::info!("🎯> cd {}", self.path); + println!(""); + log::info!("💡 Project initialized:"); + log::info!("🎯> cd ./{}", self.name); log::info!("🎯> dioxus serve"); Ok(()) diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 586d49b7c..7d580f7fb 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,4 +1,4 @@ -use crate::{cfg::ConfigOptsServe, server}; +use crate::{cfg::ConfigOptsServe, gen_page, server}; use std::{io::Write, path::PathBuf}; use structopt::StructOpt; @@ -16,19 +16,31 @@ impl Serve { // change the relase state. 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.crate_dir.join(dist_path).join("index.html"))?; + let mut file = std::fs::File::create( + crate_config + .crate_dir + .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())?; // start the develop server - server::startup(crate_config.clone(), &self.serve).await?; + server::startup(crate_config.clone()).await?; Ok(()) } + + pub fn regen_page() {} } diff --git a/src/config.rs b/src/config.rs index 9d38160f2..39a3d2038 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,43 +1,76 @@ use crate::error::Result; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; - +use std::{fs::File, io::Read, path::PathBuf}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DioxusConfig { - application: ApplicationConfig, - web: WebConfig, + pub application: ApplicationConfig, + pub web: WebConfig, +} + +impl DioxusConfig { + pub fn load() -> crate::error::Result { + 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::(&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)] pub struct ApplicationConfig { - name: String, - platform: Vec, + pub name: String, + pub platforms: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebConfig { - app: WebAppConfing, - watcher: WebWatcherConfing, - resource: WebResourceConfing, + pub app: WebAppConfing, + pub watcher: WebWatcherConfing, + pub resource: WebResourceConfing, } - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebAppConfing { - + pub title: Option, + pub out_dir: Option, + pub static_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebWatcherConfing { - + pub watch_path: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebResourceConfing { - + pub dev: WebDevResourceConfing, + pub style: Option>, + pub script: Option>, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebDevResourceConfing { + pub style: Option>, + pub script: Option>, +} #[derive(Debug, Clone)] pub struct CrateConfig { @@ -48,6 +81,7 @@ pub struct CrateConfig { pub static_dir: PathBuf, pub manifest: cargo_toml::Manifest, pub executable: ExecutableType, + pub dioxus_config: DioxusConfig, pub release: bool, } @@ -60,12 +94,23 @@ pub enum ExecutableType { impl CrateConfig { pub fn new() -> Result { + let dioxus_config = DioxusConfig::load()?; + let crate_dir = crate::cargo::crate_root()?; let workspace_dir = crate::cargo::workspace_root()?; 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 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(); @@ -96,6 +141,7 @@ impl CrateConfig { manifest, executable, release, + dioxus_config, }) } @@ -124,4 +170,4 @@ impl CrateConfig { // self.release = options.release; // self.out_dir = tempfile::Builder::new().tempdir().expect("").into_path(); // } -} \ No newline at end of file +} diff --git a/src/server/err.html b/src/server/err.html deleted file mode 100644 index 8fdbf9bae..000000000 --- a/src/server/err.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - -
-

- Sorry, but building your application failed. -

-

- Here's the error: -

- - {err} - -
- - - diff --git a/src/server/mod.rs b/src/server/mod.rs index 6b91a2e71..4d33f524e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -13,7 +13,7 @@ use std::{ }; use tower_http::services::ServeDir; -use crate::{builder, CrateConfig}; +use crate::{builder, crate_root, CrateConfig}; struct WsRelodState { update: bool, @@ -25,42 +25,54 @@ impl WsRelodState { } } -pub async fn startup( - config: CrateConfig, - opts: &crate::cfg::ConfigOptsServe, -) -> anyhow::Result<()> { +pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { log::info!("🚀 Starting development server..."); 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 - let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); + let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); watcher - .watch( - config.crate_dir.join("src").clone(), - notify::RecursiveMode::Recursive, - ) + .watch(config.crate_dir.clone(), notify::RecursiveMode::Recursive) .unwrap(); let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false })); let watcher_conf = config.clone(); let watcher_ws_state = ws_reload_state.clone(); - let watcher_dist_path = dist_path.clone(); 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 { if let Ok(v) = rx.recv() { match v { - DebouncedEvent::Create(_) - | DebouncedEvent::Write(_) - | DebouncedEvent::Remove(_) - | DebouncedEvent::Rename(_, _) => { - if let Ok(_) = builder::build(&watcher_conf, watcher_dist_path.clone()) { - // change the websocket reload state to true; - // the page will auto-reload. - watcher_ws_state.lock().unwrap().change(); + DebouncedEvent::Create(e) + | DebouncedEvent::Write(e) + | DebouncedEvent::Remove(e) + | DebouncedEvent::Rename(e, _) => { + let mut reload = false; + for path in &allow_watch_path { + let temp = crate_dir.clone().join(path); + 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() - .route("/ws", get(ws_handler)) + .route("/_dioxus/ws", get(ws_handler)) .fallback( get_service(ServeDir::new(config.crate_dir.join(&dist_path))).handle_error( |error: std::io::Error| async move { diff --git a/src/server/serve.html b/src/server/serve.html deleted file mode 100644 index b5268bd68..000000000 --- a/src/server/serve.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - Dioxus-CLI Dev Server - - -
- - - - - - - diff --git a/src/template/config.toml b/src/template/config.toml index 9644babc1..ea1efbd4b 100644 --- a/src/template/config.toml +++ b/src/template/config.toml @@ -1,9 +1,38 @@ -[package] -name = "{project}" -version = "0.1.0" -edition = "2021" +[application] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +# App (Project) Name +name = "{project-name}" -[dependencies] -dioxus = { version = "0.1.7", features = ["web"] } \ No newline at end of file +# Dioxus App Support Platform +# 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 = [] \ No newline at end of file diff --git a/src/template/default.rs b/src/template/default.rs index ac1614fc4..47b4a2c1d 100644 --- a/src/template/default.rs +++ b/src/template/default.rs @@ -8,8 +8,8 @@ fn app(cx: Scope) -> Element { cx.render(rsx! ( div { style: "text-align: center;", - h2 { "🌗🚀 Dioxus" } - p { strong { "Frontend that scales." } } + h1 { "🌗 Dioxus 🚀" } + h3 { "Frontend that scales." } p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." } } )) From 7343af2089ae0ff1f3da6b418d8168651a201832 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 23 Jan 2022 22:50:23 +0800 Subject: [PATCH 027/516] feat: `clean` command --- src/assets/index.html | 4 ++-- src/builder.rs | 30 ++++++++++++++++++------- src/cli/build/mod.rs | 22 +++++++++++++++++- src/cli/clean/mod.rs | 35 +++++++++++++++++++++++++++++ src/cli/mod.rs | 3 +-- src/cli/serve/mod.rs | 17 +++++++++----- src/config.rs | 48 ++++++++++++++++++++++++++++++++-------- src/main.rs | 8 ++++--- src/server/mod.rs | 11 ++++++++- src/template/config.toml | 8 +++++-- 10 files changed, 152 insertions(+), 34 deletions(-) diff --git a/src/assets/index.html b/src/assets/index.html index 12e1e5678..9934e490d 100644 --- a/src/assets/index.html +++ b/src/assets/index.html @@ -10,8 +10,8 @@
{script_include} diff --git a/src/builder.rs b/src/builder.rs index 381ef8b28..0ecde96cd 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -19,7 +19,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { out_dir, crate_dir, target_dir, - static_dir, + public_dir, executable, dioxus_config, .. @@ -56,7 +56,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { } // [2] Establish the output directory structure - let bindgen_outdir = out_dir.join("assets"); + let bindgen_outdir = out_dir.join("assets").join("dioxus"); // [3] Bindgen the final binary for use easy linking let mut bindgen_builder = Bindgen::new(); @@ -87,12 +87,26 @@ pub fn build(config: &CrateConfig) -> Result<()> { .out_name(&dioxus_config.application.name) .generate(&bindgen_outdir)?; - let copy_options = fs_extra::dir::CopyOptions::new(); - if static_dir.is_dir() { - match fs_extra::dir::copy(static_dir, out_dir, ©_options) { - Ok(_) => {} - Err(_e) => { - log::warn!("Error copying dir: {}", _e); + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + skip_exist: false, + buffer_size: 64000, + copy_inside: false, + content_only: false, + depth: 0, + }; + if public_dir.is_dir() { + for entry in std::fs::read_dir(&public_dir)? { + let path = entry?.path(); + if path.is_file() { + std::fs::copy(&path, out_dir.join(path.file_name().unwrap()))?; + } else { + match fs_extra::dir::copy(&path, out_dir, ©_options) { + Ok(_) => {} + Err(_e) => { + log::warn!("Error copying dir: {}", _e); + } + } } } } diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index fc329a305..e24a99822 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,4 +1,6 @@ -use crate::cfg::ConfigOptsBuild; +use std::{io::Write, path::PathBuf}; + +use crate::{cfg::ConfigOptsBuild, gen_page}; use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. @@ -18,6 +20,24 @@ impl Build { crate::builder::build(&crate_config)?; + let temp = gen_page(&crate_config.dioxus_config, false); + + let mut file = std::fs::File::create( + crate_config + .crate_dir + .join( + crate_config + .dioxus_config + .web + .app + .out_dir + .clone() + .unwrap_or(PathBuf::from("dist")), + ) + .join("index.html"), + )?; + file.write_all(temp.as_bytes())?; + Ok(()) } } diff --git a/src/cli/clean/mod.rs b/src/cli/clean/mod.rs index 1edcd053a..4ab7653d2 100644 --- a/src/cli/clean/mod.rs +++ b/src/cli/clean/mod.rs @@ -1,6 +1,41 @@ +use std::{ + fs::remove_dir_all, + path::PathBuf, + process::{Command, Stdio}, +}; + use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, StructOpt)] #[structopt(name = "clean")] pub struct Clean {} + +impl Clean { + pub fn clean(self) -> anyhow::Result<()> { + let crate_config = crate::CrateConfig::new()?; + + let output = Command::new("cargo") + .arg("clean") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output()?; + + if !output.status.success() { + log::error!("Cargo clean failed."); + return Ok(()); + } + + let out_dir = crate_config + .dioxus_config + .web + .app + .out_dir + .unwrap_or(PathBuf::from("dist")); + if crate_config.crate_dir.join(&out_dir).is_dir() { + remove_dir_all(crate_config.crate_dir.join(&out_dir))?; + } + + Ok(()) + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 93cbfef28..6224cea32 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -37,8 +37,7 @@ pub enum Commands { /// Init a new project for Dioxus. Init(init::Init), // /// Clean output artifacts. - // Clean(clean::Clean), - + Clean(clean::Clean), // /// Trunk config controls. // Config(config::Config), } diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 7d580f7fb..19a7f28ff 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,4 +1,4 @@ -use crate::{cfg::ConfigOptsServe, gen_page, server}; +use crate::{cfg::ConfigOptsServe, gen_page, server, CrateConfig}; use std::{io::Write, path::PathBuf}; use structopt::StructOpt; @@ -18,6 +18,16 @@ impl Serve { crate::builder::build(&crate_config).expect("build failed"); + // generate dev-index page + Serve::regen_dev_page(&crate_config)?; + + // start the develop server + server::startup(crate_config.clone()).await?; + + Ok(()) + } + + pub fn regen_dev_page(crate_config: &CrateConfig) -> anyhow::Result<()> { let serve_html = gen_page(&crate_config.dioxus_config, true); let mut file = std::fs::File::create( @@ -36,11 +46,6 @@ impl Serve { )?; file.write_all(serve_html.as_bytes())?; - // start the develop server - server::startup(crate_config.clone()).await?; - Ok(()) } - - pub fn regen_page() {} } diff --git a/src/config.rs b/src/config.rs index 39a3d2038..403ad0e81 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,9 +13,8 @@ impl 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(), - )); + log::warn!("Config file: `Dioxus.toml` not found."); + return Ok(Self::default()); } let mut dioxus_conf_file = File::open(crate_dir.join("Dioxus.toml"))?; @@ -34,6 +33,36 @@ impl DioxusConfig { } } +impl Default for DioxusConfig { + fn default() -> Self { + Self { + application: ApplicationConfig { + name: "dioxus".into(), + platforms: vec![String::from("web")], + }, + web: WebConfig { + app: WebAppConfing { + title: Some("dioxus | ⛺".into()), + out_dir: Some(PathBuf::from("dist")), + public_dir: Some(PathBuf::from("public")), + }, + watcher: WebWatcherConfing { + watch_path: Some(vec![PathBuf::from("src")]), + reload_html: Some(false), + }, + resource: WebResourceConfing { + dev: WebDevResourceConfing { + style: Some(vec![]), + script: Some(vec![]), + }, + style: Some(vec![]), + script: Some(vec![]), + }, + }, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApplicationConfig { pub name: String, @@ -51,12 +80,13 @@ pub struct WebConfig { pub struct WebAppConfing { pub title: Option, pub out_dir: Option, - pub static_dir: Option, + pub public_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebWatcherConfing { pub watch_path: Option>, + pub reload_html: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -78,7 +108,7 @@ pub struct CrateConfig { pub crate_dir: PathBuf, pub workspace_dir: PathBuf, pub target_dir: PathBuf, - pub static_dir: PathBuf, + pub public_dir: PathBuf, pub manifest: cargo_toml::Manifest, pub executable: ExecutableType, pub dioxus_config: DioxusConfig, @@ -107,9 +137,9 @@ impl CrateConfig { let cargo_def = &crate_dir.join("Cargo.toml"); - let static_dir = match dioxus_config.web.app.static_dir { + let public_dir = match dioxus_config.web.app.public_dir { Some(ref v) => crate_dir.join(v), - None => crate_dir.join("static"), + None => crate_dir.join("public"), }; let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap(); @@ -124,7 +154,7 @@ impl CrateConfig { (&manifest) .package .as_ref() - .and_then(|pkg| Some(pkg.name.replace("-", "_"))) + .and_then(|pkg| Some(pkg.name.clone())) .clone() }) .expect("No lib found from cargo metadata"); @@ -137,7 +167,7 @@ impl CrateConfig { crate_dir, workspace_dir, target_dir, - static_dir, + public_dir, manifest, executable, release, diff --git a/src/main.rs b/src/main.rs index 69636c6e3..70f92cf95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,9 +19,11 @@ async fn main() -> Result<()> { } } - // Commands::Clean(_) => { - // // - // } + Commands::Clean(opts) => { + if let Err(e) = opts.clean() { + log::error!("clean error: {}", e); + } + } // Commands::Config(_) => { // // diff --git a/src/server/mod.rs b/src/server/mod.rs index 4d33f524e..3151975d5 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -13,7 +13,7 @@ use std::{ }; use tower_http::services::ServeDir; -use crate::{builder, crate_root, CrateConfig}; +use crate::{builder, serve::Serve, CrateConfig}; struct WsRelodState { update: bool, @@ -71,6 +71,15 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { if let Ok(_) = builder::build(&watcher_conf) { // change the websocket reload state to true; // the page will auto-reload. + if watcher_conf + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&watcher_conf); + } watcher_ws_state.lock().unwrap().change(); } } diff --git a/src/template/config.toml b/src/template/config.toml index ea1efbd4b..44f5baabd 100644 --- a/src/template/config.toml +++ b/src/template/config.toml @@ -15,11 +15,15 @@ title = "dioxus | ⛺" # Web `build` & `serve` dist path out_dir = "dist" -# resource (static) file folder -static_dir = "static" +# resource (public) file folder +public_dir = "public" [web.watcher] +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring watch_path = ["src"] # include `assets` in web platform From bdcc06648b0275404b8626bcae6acf62b8af7af8 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 24 Jan 2022 05:55:40 +0800 Subject: [PATCH 028/516] feat: init use `cargo-generate ` --- Cargo.toml | 3 +- Dioxus.toml | 2 +- src/cli/build/mod.rs | 4 ++ src/cli/cfg.rs | 8 ++++ src/cli/init/mod.rs | 97 ++++++++++++++++++++++------------------ src/cli/serve/mod.rs | 5 +++ src/template/config.toml | 42 ----------------- src/template/default.rs | 16 ------- 8 files changed, 74 insertions(+), 103 deletions(-) delete mode 100644 src/template/config.toml delete mode 100644 src/template/default.rs diff --git a/Cargo.toml b/Cargo.toml index 8bb6fd3ce..68dd3ee56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-cli" -version = "0.1.1" +version = "0.1.2" authors = ["Jonathan Kelley"] edition = "2018" description = "CLI tool for developing, testing, and publishing Dioxus apps" @@ -28,6 +28,7 @@ structopt = "0.3.25" cargo_metadata = "0.14.1" tokio = { version = "1.15.0", features = ["full"] } atty = "0.2.14" +regex = "1.5.4" axum = { version = "0.4.4", features = ["ws", "headers"] } tower-http = { version = "0.2.0", features = ["fs", "trace"] } diff --git a/Dioxus.toml b/Dioxus.toml index abd2d1ee4..b4ba03f9d 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -13,7 +13,7 @@ platforms = ["web"] out_dir = "dist" # resource (static) file folder -static_dir = "static" +public_dir = "public" [web.watcher] diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index e24a99822..cbf82b339 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -18,6 +18,10 @@ impl Build { // change the relase state. crate_config.with_release(self.build.release); + if self.build.example.is_some() { + crate_config.as_example(self.build.example.unwrap()); + } + crate::builder::build(&crate_config)?; let temp = gen_page(&crate_config.dioxus_config, false); diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index ab68ce4f9..6a27c4890 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -16,6 +16,10 @@ pub struct ConfigOptsBuild { #[serde(default)] pub release: bool, + /// Build a example [default: ""] + #[structopt(long)] + pub example: Option, + /// Optional pattern for the app loader script [default: None] /// /// Patterns should include the sequences `{base}`, `{wasm}`, and `{js}` in order to @@ -62,6 +66,10 @@ pub struct ConfigOptsServe { #[structopt(parse(from_os_str))] pub target: Option, + /// Build a example [default: ""] + #[structopt(long)] + pub example: Option, + /// Build in release mode [default: false] #[structopt(long)] #[serde(default)] diff --git a/src/cli/init/mod.rs b/src/cli/init/mod.rs index 52dcf4c14..a19ee68fd 100644 --- a/src/cli/init/mod.rs +++ b/src/cli/init/mod.rs @@ -1,16 +1,18 @@ use std::{ - fs::{File, OpenOptions}, - io::Write, + fs::File, + io::{Read, Write}, path::PathBuf, process::{Command, Stdio}, }; +use regex::Regex; +use serde::Deserialize; use structopt::StructOpt; -use crate::error::{Error, Result}; +use crate::{error::Result, Error}; /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Default, Deserialize, StructOpt)] #[structopt(name = "init")] pub struct Init { /// Init project name @@ -18,69 +20,73 @@ pub struct Init { name: String, /// Template path - #[structopt(default_value = "default", long)] + #[structopt(default_value = "gh:dioxuslabs/dioxus-template", long)] template: String, + + /// Create a `lib` project, include a `hello-world` example. + #[structopt(long)] + #[serde(default)] + pub lib: bool, } impl Init { pub fn init(self) -> Result<()> { - if self.name.contains(".") { + if Self::name_vaild_check(self.name.clone()) { log::error!("❗Unsupported project name."); return Ok(()); } - 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() { - log::warn!("Folder '{}' is initialized.", self.name); - return Ok(()); + if project_path.join("Dioxus.toml").is_file() || project_path.join("Cargo.toml").is_file() { + return Err(Error::Other(anyhow::anyhow!( + "🧨 Folder '{}' is initialized.", + &self.name + ))); } + log::info!("🔧 Start to init a new project '{}'.", self.name); + let output = Command::new("cargo") - .arg("init") - .arg(&format!("./{}", self.name)) - .arg("--bin") + .arg("generate") + .arg("--help") .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output()?; if !output.status.success() { - return Err(Error::CargoError("Cargo init failed".into())); + log::warn!("Tool is not installed: cargo-generate, try to install it."); + let install_output = Command::new("cargo") + .arg("install") + .arg("cargo-generate") + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .output()?; + if !install_output.status.success() { + return Err(Error::Other(anyhow::anyhow!( + "Try to install cargo-generate failed." + ))); + } } - // get the template code - let template_str = match self.template { - _ => include_str!("../../template/default.rs"), - }; + let generate_output = Command::new("cargo") + .arg("generate") + .arg(&self.template) + .arg("--name") + .arg(&self.name) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .output()?; - let main_rs_file = project_path.join("src").join("main.rs"); - if !main_rs_file.is_file() { - return Err(Error::FailedToWrite); + if !generate_output.status.success() { + return Err(Error::Other(anyhow::anyhow!("Generate project failed."))); } - let mut file = File::create(main_rs_file)?; - 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."); - - if !Command::new("cargo") - .args(["add", "dioxus", "--features web"]) - .output()? - .status - .success() - { - let mut file = OpenOptions::new() - .append(true) - .open(project_path.join("Cargo.toml"))?; - file.write_all("dioxus = { version = \"0.1.7\", features = [\"web\"] }".as_bytes())?; - } + let mut dioxus_file = File::open(project_path.join("Dioxus.toml"))?; + let mut meta_file = String::new(); + dioxus_file.read_to_string(&mut meta_file)?; + meta_file = meta_file.replace("{{project-name}}", &self.name); + File::create(project_path.join("Dioxus.toml"))?.write_all(meta_file.as_bytes())?; println!(""); log::info!("💡 Project initialized:"); @@ -89,4 +95,9 @@ impl Init { Ok(()) } + + fn name_vaild_check(name: String) -> bool { + let r = Regex::new(r"^[a-zA-Z][a-zA-Z0-9\-_]$").unwrap(); + r.is_match(&name) + } } diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 19a7f28ff..57716ac7e 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -13,9 +13,14 @@ pub struct Serve { impl Serve { pub async fn serve(self) -> anyhow::Result<()> { let mut crate_config = crate::CrateConfig::new()?; + // change the relase state. crate_config.with_release(self.serve.release); + if self.serve.example.is_some() { + crate_config.as_example(self.serve.example.unwrap()); + } + crate::builder::build(&crate_config).expect("build failed"); // generate dev-index page diff --git a/src/template/config.toml b/src/template/config.toml deleted file mode 100644 index 44f5baabd..000000000 --- a/src/template/config.toml +++ /dev/null @@ -1,42 +0,0 @@ -[application] - -# App (Project) Name -name = "{project-name}" - -# Dioxus App Support Platform -# desktop, web, mobile, ssr -platforms = ["web"] - -[web.app] - -# HTML title tag content -title = "dioxus | ⛺" - -# Web `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -public_dir = "public" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -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 = [] \ No newline at end of file diff --git a/src/template/default.rs b/src/template/default.rs deleted file mode 100644 index 47b4a2c1d..000000000 --- a/src/template/default.rs +++ /dev/null @@ -1,16 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - dioxus::web::launch(app); -} - -fn app(cx: Scope) -> Element { - cx.render(rsx! ( - div { - style: "text-align: center;", - h1 { "🌗 Dioxus 🚀" } - h3 { "Frontend that scales." } - p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." } - } - )) -} From 0ff4881d54f03115812ddeda48fa4687683be8c3 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 24 Jan 2022 08:55:52 +0800 Subject: [PATCH 029/516] feat: support `desktop` build --- src/cli/build/mod.rs | 63 +++++++++++++++++++++++++++++++++++++++++++- src/cli/cfg.rs | 41 +++------------------------- 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index cbf82b339..82060b436 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,6 +1,7 @@ -use std::{io::Write, path::PathBuf}; +use std::{io::Write, path::PathBuf, process::Command, fs::copy}; use crate::{cfg::ConfigOptsBuild, gen_page}; +use std::fs::remove_dir_all; use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. @@ -22,6 +23,66 @@ impl Build { crate_config.as_example(self.build.example.unwrap()); } + if self.build.platform.is_some() { + if self.build.platform.unwrap().to_uppercase() == "DESKTOP" { + log::info!("🚅 Running build command..."); + + let mut cmd = Command::new("cargo"); + cmd.current_dir(&crate_config.crate_dir) + .arg("build") + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + + if self.build.release { + cmd.arg("--release"); + } + + match &crate_config.executable { + crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), + crate::ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), + crate::ExecutableType::Example(name) => cmd.arg("--example").arg(name), + }; + + let output = cmd.output()?; + + if output.status.success() { + if crate_config.out_dir.is_dir() { + + remove_dir_all(&crate_config.out_dir)?; + + let release_type = match crate_config.release { + true => "release", + false => "debug", + }; + + let mut res_path = match &crate_config.executable { + crate::ExecutableType::Binary(name) + | crate::ExecutableType::Lib(name) => crate_config + .target_dir + .join(format!("{}", release_type)) + .join(format!("{}", name)), + + crate::ExecutableType::Example(name) => crate_config + .target_dir + .join(format!("{}/examples", release_type)) + .join(format!("{}", name)), + }; + + let target_file; + if cfg!(windows) { + res_path.set_extension("exe"); + target_file = format!("{}.exe", &crate_config.dioxus_config.application.name); + } else { + target_file = crate_config.dioxus_config.application.name.clone(); + } + copy(res_path, &crate_config.out_dir.join(target_file))?; + } + } + + return Ok(()); + } + } + crate::builder::build(&crate_config)?; let temp = gen_page(&crate_config.dioxus_config, false); diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 6a27c4890..7010d62e6 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -20,44 +20,9 @@ pub struct ConfigOptsBuild { #[structopt(long)] pub example: Option, - /// Optional pattern for the app loader script [default: None] - /// - /// Patterns should include the sequences `{base}`, `{wasm}`, and `{js}` in order to - /// properly load the application. Other sequences may be included corresponding - /// to key/value pairs provided in `pattern_params`. - /// - /// These values can only be provided via config file. - #[structopt(skip)] - #[serde(default)] - pub pattern_script: Option, - - /// Optional pattern for the app preload element [default: None] - /// - /// Patterns should include the sequences `{base}`, `{wasm}`, and `{js}` in order to - /// properly preload the application. Other sequences may be included corresponding - /// to key/value pairs provided in `pattern_params`. - /// - /// These values can only be provided via config file. - #[structopt(skip)] - #[serde(default)] - pub pattern_preload: Option, - - #[structopt(skip)] - #[serde(default)] - /// Optional replacement parameters corresponding to the patterns provided in - /// `pattern_script` and `pattern_preload`. - /// - /// When a pattern is being replaced with its corresponding value from this map, if the value is - /// prefixed with the symbol `@`, then the value is expected to be a file path, and the pattern - /// will be replaced with the contents of the target file. This allows insertion of some big JSON - /// state or even HTML files as a part of the `index.html` build. - /// - /// Trunk will automatically insert the `base`, `wasm` and `js` key/values into this map. In order - //// for the app to be loaded properly, the patterns `{base}`, `{wasm}` and `{js}` should be used - /// in `pattern_script` and `pattern_preload`. - /// - /// These values can only be provided via config file. - pub pattern_params: Option>, + /// Build platform: support Web & Desktop [default: "web"] + #[structopt(long)] + pub platform: Option, } #[derive(Clone, Debug, Default, Deserialize, StructOpt)] From bb69a040456db2e2a669ba4e3d80f2d2da44415f Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 24 Jan 2022 11:29:39 +0800 Subject: [PATCH 030/516] feat: commit code --- Dioxus.toml | 4 +-- src/cli/build/mod.rs | 76 ++++++++++++++++++++++++++++---------------- src/cli/cfg.rs | 1 - src/cli/clean/mod.rs | 3 +- src/cli/serve/mod.rs | 3 +- src/config.rs | 6 ++-- 6 files changed, 55 insertions(+), 38 deletions(-) diff --git a/Dioxus.toml b/Dioxus.toml index b4ba03f9d..86f52c349 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -7,11 +7,11 @@ name = "dioxus-cli" # desktop, web, mobile, ssr platforms = ["web"] -[web.app] - # Web `build` & `serve` dist path out_dir = "dist" +[web.app] + # resource (static) file folder public_dir = "public" diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 82060b436..c1639e815 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,4 +1,9 @@ -use std::{io::Write, path::PathBuf, process::Command, fs::copy}; +use std::{ + fs::{copy, create_dir_all}, + io::Write, + path::PathBuf, + process::Command, +}; use crate::{cfg::ConfigOptsBuild, gen_page}; use std::fs::remove_dir_all; @@ -25,7 +30,7 @@ impl Build { if self.build.platform.is_some() { if self.build.platform.unwrap().to_uppercase() == "DESKTOP" { - log::info!("🚅 Running build command..."); + log::info!("🚅 Running build [Desktop] command..."); let mut cmd = Command::new("cargo"); cmd.current_dir(&crate_config.crate_dir) @@ -47,36 +52,52 @@ impl Build { if output.status.success() { if crate_config.out_dir.is_dir() { - remove_dir_all(&crate_config.out_dir)?; + } - let release_type = match crate_config.release { - true => "release", - false => "debug", - }; - - let mut res_path = match &crate_config.executable { - crate::ExecutableType::Binary(name) - | crate::ExecutableType::Lib(name) => crate_config + let release_type = match crate_config.release { + true => "release", + false => "debug", + }; + + let file_name: String; + let mut res_path = match &crate_config.executable { + crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => { + file_name = name.clone(); + crate_config .target_dir .join(format!("{}", release_type)) - .join(format!("{}", name)), - - crate::ExecutableType::Example(name) => crate_config - .target_dir - .join(format!("{}/examples", release_type)) - .join(format!("{}", name)), - }; - - let target_file; - if cfg!(windows) { - res_path.set_extension("exe"); - target_file = format!("{}.exe", &crate_config.dioxus_config.application.name); - } else { - target_file = crate_config.dioxus_config.application.name.clone(); + .join(format!("{}", name)) } - copy(res_path, &crate_config.out_dir.join(target_file))?; + crate::ExecutableType::Example(name) => { + file_name = name.clone(); + crate_config + .target_dir + .join(format!("{}", release_type)) + .join("examples") + .join(format!("{}", name)) + } + }; + + let target_file; + if cfg!(windows) { + res_path.set_extension("exe"); + target_file = format!("{}.exe", &file_name); + } else { + target_file = file_name.clone(); } + create_dir_all(&crate_config.out_dir)?; + copy(res_path, &crate_config.out_dir.join(target_file))?; + + log::info!( + "🏛 Build completed: [:{}]", + &crate_config + .dioxus_config + .application + .out_dir + .unwrap_or(PathBuf::from("dist")) + .display() + ); } return Ok(()); @@ -93,8 +114,7 @@ impl Build { .join( crate_config .dioxus_config - .web - .app + .application .out_dir .clone() .unwrap_or(PathBuf::from("dist")), diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 7010d62e6..0734f362d 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -2,7 +2,6 @@ use std::path::PathBuf; use structopt::StructOpt; use serde::Deserialize; -use std::collections::HashMap; /// Config options for the build system. #[derive(Clone, Debug, Default, Deserialize, StructOpt)] diff --git a/src/cli/clean/mod.rs b/src/cli/clean/mod.rs index 4ab7653d2..7ba33b530 100644 --- a/src/cli/clean/mod.rs +++ b/src/cli/clean/mod.rs @@ -28,8 +28,7 @@ impl Clean { let out_dir = crate_config .dioxus_config - .web - .app + .application .out_dir .unwrap_or(PathBuf::from("dist")); if crate_config.crate_dir.join(&out_dir).is_dir() { diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 57716ac7e..8b002e1fb 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -41,8 +41,7 @@ impl Serve { .join( crate_config .dioxus_config - .web - .app + .application .out_dir .clone() .unwrap_or(PathBuf::from("dist")), diff --git a/src/config.rs b/src/config.rs index 403ad0e81..ad1e25ea5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -39,11 +39,11 @@ impl Default for DioxusConfig { application: ApplicationConfig { name: "dioxus".into(), platforms: vec![String::from("web")], + out_dir: Some(PathBuf::from("dist")), }, web: WebConfig { app: WebAppConfing { title: Some("dioxus | ⛺".into()), - out_dir: Some(PathBuf::from("dist")), public_dir: Some(PathBuf::from("public")), }, watcher: WebWatcherConfing { @@ -67,6 +67,7 @@ impl Default for DioxusConfig { pub struct ApplicationConfig { pub name: String, pub platforms: Vec, + pub out_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -79,7 +80,6 @@ pub struct WebConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebAppConfing { pub title: Option, - pub out_dir: Option, pub public_dir: Option, } @@ -130,7 +130,7 @@ impl CrateConfig { let workspace_dir = crate::cargo::workspace_root()?; let target_dir = workspace_dir.join("target"); - let out_dir = match dioxus_config.web.app.out_dir { + let out_dir = match dioxus_config.application.out_dir { Some(ref v) => crate_dir.join(v), None => crate_dir.join("dist"), }; From d6957774026620c8649e92202e312432580c771c Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 24 Jan 2022 11:55:27 +0800 Subject: [PATCH 031/516] feat: support `desktop` --- src/builder.rs | 81 +++++++++++++++++++++++++++++++++++++++++++- src/cli/build/mod.rs | 80 ++----------------------------------------- src/cli/cfg.rs | 4 +++ src/cli/serve/mod.rs | 25 +++++++++++++- 4 files changed, 110 insertions(+), 80 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 0ecde96cd..415e56480 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -3,7 +3,11 @@ use crate::{ error::{Error, Result}, DioxusConfig, }; -use std::process::Command; +use std::{ + fs::{copy, create_dir_all, remove_dir_all}, + path::PathBuf, + process::Command, +}; use wasm_bindgen_cli_support::Bindgen; pub fn build(config: &CrateConfig) -> Result<()> { @@ -116,6 +120,81 @@ pub fn build(config: &CrateConfig) -> Result<()> { Ok(()) } +pub fn build_desktop(config: &CrateConfig) -> Result<()> { + log::info!("🚅 Running build [Desktop] command..."); + + let mut cmd = Command::new("cargo"); + cmd.current_dir(&config.crate_dir) + .arg("build") + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + + if config.release { + cmd.arg("--release"); + } + + match &config.executable { + crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), + crate::ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), + crate::ExecutableType::Example(name) => cmd.arg("--example").arg(name), + }; + + let output = cmd.output()?; + + if output.status.success() { + if config.out_dir.is_dir() { + remove_dir_all(&config.out_dir)?; + } + + let release_type = match config.release { + true => "release", + false => "debug", + }; + + let file_name: String; + let mut res_path = match &config.executable { + crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => { + file_name = name.clone(); + config + .target_dir + .join(format!("{}", release_type)) + .join(format!("{}", name)) + } + crate::ExecutableType::Example(name) => { + file_name = name.clone(); + config + .target_dir + .join(format!("{}", release_type)) + .join("examples") + .join(format!("{}", name)) + } + }; + + let target_file; + if cfg!(windows) { + res_path.set_extension("exe"); + target_file = format!("{}.exe", &file_name); + } else { + target_file = file_name.clone(); + } + create_dir_all(&config.out_dir)?; + copy(res_path, &config.out_dir.join(target_file))?; + + log::info!( + "🚩 Build completed: [./{}]", + config + .dioxus_config + .application + .out_dir + .clone() + .unwrap_or(PathBuf::from("dist")) + .display() + ); + } + + Ok(()) +} + pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { let mut html = String::from(include_str!("./assets/index.html")); diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index c1639e815..f018165ef 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,12 +1,6 @@ -use std::{ - fs::{copy, create_dir_all}, - io::Write, - path::PathBuf, - process::Command, -}; +use std::{io::Write, path::PathBuf}; use crate::{cfg::ConfigOptsBuild, gen_page}; -use std::fs::remove_dir_all; use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. @@ -30,77 +24,7 @@ impl Build { if self.build.platform.is_some() { if self.build.platform.unwrap().to_uppercase() == "DESKTOP" { - log::info!("🚅 Running build [Desktop] command..."); - - let mut cmd = Command::new("cargo"); - cmd.current_dir(&crate_config.crate_dir) - .arg("build") - .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()); - - if self.build.release { - cmd.arg("--release"); - } - - match &crate_config.executable { - crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), - crate::ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), - crate::ExecutableType::Example(name) => cmd.arg("--example").arg(name), - }; - - let output = cmd.output()?; - - if output.status.success() { - if crate_config.out_dir.is_dir() { - remove_dir_all(&crate_config.out_dir)?; - } - - let release_type = match crate_config.release { - true => "release", - false => "debug", - }; - - let file_name: String; - let mut res_path = match &crate_config.executable { - crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => { - file_name = name.clone(); - crate_config - .target_dir - .join(format!("{}", release_type)) - .join(format!("{}", name)) - } - crate::ExecutableType::Example(name) => { - file_name = name.clone(); - crate_config - .target_dir - .join(format!("{}", release_type)) - .join("examples") - .join(format!("{}", name)) - } - }; - - let target_file; - if cfg!(windows) { - res_path.set_extension("exe"); - target_file = format!("{}.exe", &file_name); - } else { - target_file = file_name.clone(); - } - create_dir_all(&crate_config.out_dir)?; - copy(res_path, &crate_config.out_dir.join(target_file))?; - - log::info!( - "🏛 Build completed: [:{}]", - &crate_config - .dioxus_config - .application - .out_dir - .unwrap_or(PathBuf::from("dist")) - .display() - ); - } - - return Ok(()); + crate::builder::build_desktop(&crate_config)?; } } diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 0734f362d..454da71fe 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -38,6 +38,10 @@ pub struct ConfigOptsServe { #[structopt(long)] #[serde(default)] pub release: bool, + + /// Build platform: support Web & Desktop [default: "web"] + #[structopt(long)] + pub platform: Option, } /// Ensure the given value for `--public-url` is formatted correctly. diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 8b002e1fb..ef413a63e 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,5 +1,5 @@ use crate::{cfg::ConfigOptsServe, gen_page, server, CrateConfig}; -use std::{io::Write, path::PathBuf}; +use std::{io::Write, path::PathBuf, process::Command}; use structopt::StructOpt; /// Run the WASM project on dev-server @@ -21,6 +21,29 @@ impl Serve { crate_config.as_example(self.serve.example.unwrap()); } + if self.serve.platform.is_some() { + if self.serve.platform.unwrap().to_uppercase() == "DESKTOP" { + crate::builder::build_desktop(&crate_config)?; + + match &crate_config.executable { + crate::ExecutableType::Binary(name) + | crate::ExecutableType::Lib(name) + | crate::ExecutableType::Example(name) => { + let mut file = crate_config.out_dir.join(name); + if cfg!(windows) { + file.set_extension("exe"); + } + Command::new(format!( + "{}", + crate_config.out_dir.join(file).to_str().unwrap() + )) + .output()?; + } + } + return Ok(()); + } + } + crate::builder::build(&crate_config).expect("build failed"); // generate dev-index page From 4cc7752adf17e41e153bcfa490ea14964e67b863 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 24 Jan 2022 12:16:02 +0800 Subject: [PATCH 032/516] fix: change `init` to `create` --- src/cli/{init => create}/mod.rs | 10 +++++----- src/cli/mod.rs | 4 ++-- src/config.rs | 3 +-- src/main.rs | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) rename src/cli/{init => create}/mod.rs (94%) diff --git a/src/cli/init/mod.rs b/src/cli/create/mod.rs similarity index 94% rename from src/cli/init/mod.rs rename to src/cli/create/mod.rs index a19ee68fd..7cb2edbbe 100644 --- a/src/cli/init/mod.rs +++ b/src/cli/create/mod.rs @@ -13,8 +13,8 @@ use crate::{error::Result, Error}; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Default, Deserialize, StructOpt)] -#[structopt(name = "init")] -pub struct Init { +#[structopt(name = "create")] +pub struct Create { /// Init project name #[structopt(default_value = ".")] name: String, @@ -29,8 +29,8 @@ pub struct Init { pub lib: bool, } -impl Init { - pub fn init(self) -> Result<()> { +impl Create { + pub fn create(self) -> Result<()> { if Self::name_vaild_check(self.name.clone()) { log::error!("❗Unsupported project name."); return Ok(()); @@ -45,7 +45,7 @@ impl Init { ))); } - log::info!("🔧 Start to init a new project '{}'.", self.name); + log::info!("🔧 Start to create a new project '{}'.", self.name); let output = Command::new("cargo") .arg("generate") diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 6224cea32..91d561463 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -3,7 +3,7 @@ use structopt::StructOpt; pub mod build; pub mod cfg; pub mod clean; -pub mod init; +pub mod create; pub mod serve; pub mod translate; @@ -35,7 +35,7 @@ pub enum Commands { /// Build, watch & serve the Rust WASM app and all of its assets. Serve(serve::Serve), /// Init a new project for Dioxus. - Init(init::Init), + Create(create::Create), // /// Clean output artifacts. Clean(clean::Clean), // /// Trunk config controls. diff --git a/src/config.rs b/src/config.rs index ad1e25ea5..fa52ddcbc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,8 +13,7 @@ impl DioxusConfig { let crate_dir = crate::cargo::crate_root()?; if !crate_dir.join("Dioxus.toml").is_file() { - log::warn!("Config file: `Dioxus.toml` not found."); - return Ok(Self::default()); + return Err(crate::error::Error::Unique("Config file: `Dioxus.toml` not found.".into())); } let mut dioxus_conf_file = File::open(crate_dir.join("Dioxus.toml"))?; diff --git a/src/main.rs b/src/main.rs index 70f92cf95..20536048b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,9 +34,9 @@ async fn main() -> Result<()> { } } - Commands::Init(opts) => { - if let Err(e) = opts.init() { - log::error!("init error: {}", e); + Commands::Create(opts) => { + if let Err(e) = opts.create() { + log::error!("create error: {}", e); } } } From 66b832045ad436c4577990ef138407331d38d056 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 24 Jan 2022 16:18:16 +0800 Subject: [PATCH 033/516] feat: add `config` command --- Dioxus.toml => src/assets/dioxus.toml | 18 +++++------ src/cli/config/mod.rs | 43 +++++++++++++++++++++++++++ src/cli/create/mod.rs | 5 ---- src/cli/mod.rs | 7 +++-- src/config.rs | 4 ++- src/main.rs | 6 ++++ 6 files changed, 63 insertions(+), 20 deletions(-) rename Dioxus.toml => src/assets/dioxus.toml (79%) create mode 100644 src/cli/config/mod.rs diff --git a/Dioxus.toml b/src/assets/dioxus.toml similarity index 79% rename from Dioxus.toml rename to src/assets/dioxus.toml index 86f52c349..717b070e2 100644 --- a/Dioxus.toml +++ b/src/assets/dioxus.toml @@ -1,7 +1,7 @@ [application] # App (Project) Name -name = "dioxus-cli" +name = "{{project-name}}" # Dioxus App Support Platform # desktop, web, mobile, ssr @@ -12,31 +12,27 @@ out_dir = "dist" [web.app] +# HTML title tag content +title = "dioxus | ⛺" + # resource (static) file folder public_dir = "public" [web.watcher] - watch_path = ["src"] # include `assets` in web platform [web.resource] # CSS style file -style = [ - "./my-style.css" -] +style = [] # Javascript code file -script = [ - "./app.js" -] +script = [] [web.resource.dev] # Javascript code file # serve: [dev-server] only -script = [ - "./debug.js" -] \ No newline at end of file +script = [] \ No newline at end of file diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs new file mode 100644 index 000000000..5e903dc3a --- /dev/null +++ b/src/cli/config/mod.rs @@ -0,0 +1,43 @@ +use std::{fs::File, io::Write}; + +use serde::Deserialize; +use structopt::StructOpt; + +/// Build the Rust WASM app and all of its assets. +#[derive(Clone, Debug, Deserialize, StructOpt)] +#[structopt(name = "config")] +pub enum Config { + /// Init `Dioxus.toml` for project/folder. + Init { + /// Init project name + name: String, + + /// Cover old config + #[structopt(long)] + #[serde(default)] + force: bool, + }, +} + +impl Config { + pub fn config(self) -> anyhow::Result<()> { + let crate_root = crate::cargo::crate_root()?; + match self { + Config::Init { name, force } => { + let conf_path = crate_root.join("Dioxus.toml"); + if conf_path.is_file() && force == false { + log::warn!( + "config file `Dioxus.toml` already exist, use `--force` to overwrite it." + ); + return Ok(()); + } + let mut file = File::create(conf_path)?; + let content = String::from(include_str!("../../assets/dioxus.toml")) + .replace("{{project-name}}", &name); + file.write_all(content.as_bytes())?; + log::info!("🚩 Init config file completed."); + } + } + Ok(()) + } +} diff --git a/src/cli/create/mod.rs b/src/cli/create/mod.rs index 7cb2edbbe..42364ddab 100644 --- a/src/cli/create/mod.rs +++ b/src/cli/create/mod.rs @@ -22,11 +22,6 @@ pub struct Create { /// Template path #[structopt(default_value = "gh:dioxuslabs/dioxus-template", long)] template: String, - - /// Create a `lib` project, include a `hello-world` example. - #[structopt(long)] - #[serde(default)] - pub lib: bool, } impl Create { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 91d561463..4a72e00ac 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -3,6 +3,7 @@ use structopt::StructOpt; pub mod build; pub mod cfg; pub mod clean; +pub mod config; pub mod create; pub mod serve; pub mod translate; @@ -36,8 +37,8 @@ pub enum Commands { Serve(serve::Serve), /// Init a new project for Dioxus. Create(create::Create), - // /// Clean output artifacts. + /// Clean output artifacts. Clean(clean::Clean), - // /// Trunk config controls. - // Config(config::Config), + /// Dioxus config file controls. + Config(config::Config), } diff --git a/src/config.rs b/src/config.rs index fa52ddcbc..fb26b50bf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,7 +13,9 @@ impl DioxusConfig { let crate_dir = crate::cargo::crate_root()?; if !crate_dir.join("Dioxus.toml").is_file() { - return Err(crate::error::Error::Unique("Config file: `Dioxus.toml` not found.".into())); + return Err(crate::error::Error::Unique( + "Config file: `Dioxus.toml` not found.".into(), + )); } let mut dioxus_conf_file = File::open(crate_dir.join("Dioxus.toml"))?; diff --git a/src/main.rs b/src/main.rs index 20536048b..1ef3a08f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,12 @@ async fn main() -> Result<()> { log::error!("create error: {}", e); } } + + Commands::Config(opts) => { + if let Err(e) = opts.config() { + log::error!("config error: {}", e); + } + } } Ok(()) From 84d1867654482640fa2cf31dd3eae10bf1bfa52d Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 25 Jan 2022 12:42:27 +0800 Subject: [PATCH 034/516] fix: use clippy fix --- src/builder.rs | 18 +++++++++--------- src/cli/build/mod.rs | 6 ++---- src/cli/config/mod.rs | 2 +- src/cli/create/mod.rs | 2 +- src/cli/serve/mod.rs | 29 ++++++++++++----------------- src/cli/translate/mod.rs | 4 ++-- src/cli/translate/to_component.rs | 2 +- src/config.rs | 15 +++++++-------- src/logging.rs | 2 +- 9 files changed, 36 insertions(+), 44 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 415e56480..c281f2b80 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -157,16 +157,16 @@ pub fn build_desktop(config: &CrateConfig) -> Result<()> { file_name = name.clone(); config .target_dir - .join(format!("{}", release_type)) - .join(format!("{}", name)) + .join(release_type.to_string()) + .join(name.to_string()) } crate::ExecutableType::Example(name) => { file_name = name.clone(); config .target_dir - .join(format!("{}", release_type)) + .join(release_type.to_string()) .join("examples") - .join(format!("{}", name)) + .join(name.to_string()) } }; @@ -175,7 +175,7 @@ pub fn build_desktop(config: &CrateConfig) -> Result<()> { res_path.set_extension("exe"); target_file = format!("{}.exe", &file_name); } else { - target_file = file_name.clone(); + target_file = file_name; } create_dir_all(&config.out_dir)?; copy(res_path, &config.out_dir.join(target_file))?; @@ -200,12 +200,12 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { let resouces = config.web.resource.clone(); - let mut style_list = resouces.style.unwrap_or(vec![]); - let mut script_list = resouces.script.unwrap_or(vec![]); + let mut style_list = resouces.style.unwrap_or_default(); + let mut script_list = resouces.script.unwrap_or_default(); if serve { - let mut dev_style = resouces.dev.style.clone().unwrap_or(vec![]); - let mut dev_script = resouces.dev.script.clone().unwrap_or(vec![]); + let mut dev_style = resouces.dev.style.clone().unwrap_or_default(); + let mut dev_script = resouces.dev.script.unwrap_or_default(); style_list.append(&mut dev_style); script_list.append(&mut dev_script); } diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index f018165ef..318f6b7f5 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -22,10 +22,8 @@ impl Build { crate_config.as_example(self.build.example.unwrap()); } - if self.build.platform.is_some() { - if self.build.platform.unwrap().to_uppercase() == "DESKTOP" { - crate::builder::build_desktop(&crate_config)?; - } + if self.build.platform.is_some() && self.build.platform.unwrap().to_uppercase() == "DESKTOP" { + crate::builder::build_desktop(&crate_config)?; } crate::builder::build(&crate_config)?; diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index 5e903dc3a..d5291a7d6 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -25,7 +25,7 @@ impl Config { match self { Config::Init { name, force } => { let conf_path = crate_root.join("Dioxus.toml"); - if conf_path.is_file() && force == false { + if conf_path.is_file() && !force { log::warn!( "config file `Dioxus.toml` already exist, use `--force` to overwrite it." ); diff --git a/src/cli/create/mod.rs b/src/cli/create/mod.rs index 42364ddab..fa553c55a 100644 --- a/src/cli/create/mod.rs +++ b/src/cli/create/mod.rs @@ -83,7 +83,7 @@ impl Create { meta_file = meta_file.replace("{{project-name}}", &self.name); File::create(project_path.join("Dioxus.toml"))?.write_all(meta_file.as_bytes())?; - println!(""); + println!(); log::info!("💡 Project initialized:"); log::info!("🎯> cd ./{}", self.name); log::info!("🎯> dioxus serve"); diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index ef413a63e..04795ab13 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -21,27 +21,22 @@ impl Serve { crate_config.as_example(self.serve.example.unwrap()); } - if self.serve.platform.is_some() { - if self.serve.platform.unwrap().to_uppercase() == "DESKTOP" { - crate::builder::build_desktop(&crate_config)?; + if self.serve.platform.is_some() && self.serve.platform.unwrap().to_uppercase() == "DESKTOP" { + crate::builder::build_desktop(&crate_config)?; - match &crate_config.executable { - crate::ExecutableType::Binary(name) - | crate::ExecutableType::Lib(name) - | crate::ExecutableType::Example(name) => { - let mut file = crate_config.out_dir.join(name); - if cfg!(windows) { - file.set_extension("exe"); - } - Command::new(format!( - "{}", - crate_config.out_dir.join(file).to_str().unwrap() - )) - .output()?; + match &crate_config.executable { + crate::ExecutableType::Binary(name) + | crate::ExecutableType::Lib(name) + | crate::ExecutableType::Example(name) => { + let mut file = crate_config.out_dir.join(name); + if cfg!(windows) { + file.set_extension("exe"); } + Command::new(crate_config.out_dir.join(file).to_str().unwrap().to_string()) + .output()?; } - return Ok(()); } + return Ok(()); } crate::builder::build(&crate_config).expect("build failed"); diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 5799cad02..a62a1b4b3 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -48,7 +48,7 @@ impl Translate { exit(0); }) }); - if let None = temp { + if temp.is_none() { if let Some(s) = source { contents = s; } else { @@ -112,7 +112,7 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { } write!(f, "\",")?; } - write!(f, "\n")?; + writeln!(f)?; // write the attributes if let Some(id) = &el.id { diff --git a/src/cli/translate/to_component.rs b/src/cli/translate/to_component.rs index 972da7a25..c83777ccb 100644 --- a/src/cli/translate/to_component.rs +++ b/src/cli/translate/to_component.rs @@ -120,7 +120,7 @@ fn render_element<'a>( } write!(f, "\",")?; } - write!(f, "\n")?; + writeln!(f)?; // write the attributes if let Some(id) = &el.id { diff --git a/src/config.rs b/src/config.rs index fb26b50bf..fa7e1bd1c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -23,12 +23,12 @@ impl DioxusConfig { dioxus_conf_file.read_to_string(&mut meta_str)?; match toml::from_str::(&meta_str) { - Ok(v) => return Ok(v), + Ok(v) => Ok(v), Err(e) => { log::error!("{}", e); - return Err(crate::error::Error::Unique( + Err(crate::error::Error::Unique( "Dioxus.toml parse failed".into(), - )); + )) } } } @@ -147,16 +147,15 @@ impl CrateConfig { // We just assume they're using a 'main.rs' // Anyway, we've already parsed the manifest, so it should be easy to change the type - let output_filename = (&manifest) + let output_filename = manifest .lib .as_ref() .and_then(|lib| lib.name.clone()) .or_else(|| { - (&manifest) + manifest .package - .as_ref() - .and_then(|pkg| Some(pkg.name.clone())) - .clone() + .as_ref().map(|pkg| pkg.name.clone()) + }) .expect("No lib found from cargo metadata"); let executable = ExecutableType::Binary(output_filename); diff --git a/src/logging.rs b/src/logging.rs index d30eeee47..48c2a2788 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -14,7 +14,7 @@ pub fn set_up_logging() { // configure colors for the name of the level. // since almost all of them are the same as the color for the whole line, we // just clone `colors_line` and overwrite our changes - let colors_level = colors_line.clone().info(Color::Green); + let colors_level = colors_line.info(Color::Green); // here we set up our fern Dispatch fern::Dispatch::new() .format(move |out, message, record| { From 1c2cf3330ee164a9492342ab1132f8d17b15931b Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 25 Jan 2022 13:52:33 +0800 Subject: [PATCH 035/516] fix: use clippy fix --- src/builder.rs | 4 ++-- src/cargo.rs | 6 +++--- src/cli/build/mod.rs | 2 +- src/cli/clean/mod.rs | 2 +- src/cli/serve/mod.rs | 2 +- src/cli/translate/mod.rs | 2 +- src/server/mod.rs | 28 +++++++++++++--------------- 7 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index c281f2b80..7d76f75cc 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -187,7 +187,7 @@ pub fn build_desktop(config: &CrateConfig) -> Result<()> { .application .out_dir .clone() - .unwrap_or(PathBuf::from("dist")) + .unwrap_or_else(|| PathBuf::from("dist")) .display() ); } @@ -238,7 +238,7 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { html = html.replace("{app_name}", &config.application.name); - let title = config.web.app.title.clone().unwrap_or("dioxus | ⛺".into()); + let title = config.web.app.title.clone().unwrap_or_else(|| "dioxus | ⛺".into()); html.replace("{app_title}", &title) } diff --git a/src/cargo.rs b/src/cargo.rs index 3a911a81e..c268c5a3a 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -1,6 +1,6 @@ //! Utilities for working with cargo and rust files use crate::error::{Error, Result}; -use std::{env, fs, path::PathBuf, process::Command, str}; +use std::{env, fs, path::{PathBuf, Path}, process::Command, str}; /// How many parent folders are searched for a `Cargo.toml` const MAX_ANCESTORS: u32 = 10; @@ -44,7 +44,7 @@ pub fn workspace_root() -> Result { } let stdout = str::from_utf8(&output.stdout).unwrap(); - for line in stdout.lines() { + if let Some(line) = stdout.lines().next() { let meta: serde_json::Value = serde_json::from_str(line) .map_err(|_| Error::CargoError("InvalidOutput".to_string()))?; @@ -58,7 +58,7 @@ pub fn workspace_root() -> Result { } /// Checks if the directory contains `Cargo.toml` -fn contains_manifest(path: &PathBuf) -> bool { +fn contains_manifest(path: &Path) -> bool { fs::read_dir(path) .map(|entries| { entries diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 318f6b7f5..27015420b 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -39,7 +39,7 @@ impl Build { .application .out_dir .clone() - .unwrap_or(PathBuf::from("dist")), + .unwrap_or_else(|| PathBuf::from("dist")), ) .join("index.html"), )?; diff --git a/src/cli/clean/mod.rs b/src/cli/clean/mod.rs index 7ba33b530..62593b1ee 100644 --- a/src/cli/clean/mod.rs +++ b/src/cli/clean/mod.rs @@ -30,7 +30,7 @@ impl Clean { .dioxus_config .application .out_dir - .unwrap_or(PathBuf::from("dist")); + .unwrap_or_else(|| PathBuf::from("dist")); if crate_config.crate_dir.join(&out_dir).is_dir() { remove_dir_all(crate_config.crate_dir.join(&out_dir))?; } diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 04795ab13..a84d8136d 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -62,7 +62,7 @@ impl Serve { .application .out_dir .clone() - .unwrap_or(PathBuf::from("dist")), + .unwrap_or_else(|| PathBuf::from("dist")), ) .join("index.html"), )?; diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index a62a1b4b3..81b5092ed 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -48,7 +48,7 @@ impl Translate { exit(0); }) }); - if temp.is_none() { + if let Some(..) = temp { if let Some(s) = source { contents = s; } else { diff --git a/src/server/mod.rs b/src/server/mod.rs index 3151975d5..3092a01ec 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -49,7 +49,7 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { .watcher .watch_path .clone() - .unwrap_or(vec![PathBuf::from("src")]); + .unwrap_or_else(|| vec![PathBuf::from("src")]); let crate_dir = watcher_conf.crate_dir.clone(); loop { if let Ok(v) = rx.recv() { @@ -67,21 +67,19 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { } } - if reload { - if let Ok(_) = builder::build(&watcher_conf) { - // change the websocket reload state to true; - // the page will auto-reload. - if watcher_conf - .dioxus_config - .web - .watcher - .reload_html - .unwrap_or(false) - { - let _ = Serve::regen_dev_page(&watcher_conf); - } - watcher_ws_state.lock().unwrap().change(); + if reload && builder::build(&watcher_conf).is_ok() { + // change the websocket reload state to true; + // the page will auto-reload. + if watcher_conf + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&watcher_conf); } + watcher_ws_state.lock().unwrap().change(); } } _ => {} From 25e3ee848cadcd14ff291b39436266857037a11a Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 25 Jan 2022 17:09:06 +0800 Subject: [PATCH 036/516] fix: high CPU usage --- src/server/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/mod.rs b/src/server/mod.rs index 3092a01ec..364b7c572 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -118,6 +118,7 @@ async fn ws_handler( ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { loop { + tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; if state.lock().unwrap().update { socket .send(Message::Text(String::from("reload"))) From b01b3f519f5a2a279cb60c479e7000d9819fed33 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 25 Jan 2022 17:51:36 +0800 Subject: [PATCH 037/516] fix: commit code --- src/builder.rs | 17 ++++++++++------ src/cargo.rs | 7 ++++++- src/cli/build/mod.rs | 14 +++++++++---- src/cli/cfg.rs | 8 ++++---- src/cli/serve/mod.rs | 47 ++++++++++++++++++++++++++++---------------- src/config.rs | 12 +++-------- src/main.rs | 3 --- src/server/mod.rs | 6 +++--- 8 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 7d76f75cc..583c9bc8f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -170,13 +170,13 @@ pub fn build_desktop(config: &CrateConfig) -> Result<()> { } }; - let target_file; - if cfg!(windows) { + let target_file = if cfg!(windows) { res_path.set_extension("exe"); - target_file = format!("{}.exe", &file_name); + format!("{}.exe", &file_name) } else { - target_file = file_name; - } + file_name + }; + create_dir_all(&config.out_dir)?; copy(res_path, &config.out_dir.join(target_file))?; @@ -238,7 +238,12 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { html = html.replace("{app_name}", &config.application.name); - let title = config.web.app.title.clone().unwrap_or_else(|| "dioxus | ⛺".into()); + let title = config + .web + .app + .title + .clone() + .unwrap_or_else(|| "dioxus | ⛺".into()); html.replace("{app_title}", &title) } diff --git a/src/cargo.rs b/src/cargo.rs index c268c5a3a..67a3e48f1 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -1,6 +1,11 @@ //! Utilities for working with cargo and rust files use crate::error::{Error, Result}; -use std::{env, fs, path::{PathBuf, Path}, process::Command, str}; +use std::{ + env, fs, + path::{Path, PathBuf}, + process::Command, + str, +}; /// How many parent folders are searched for a `Cargo.toml` const MAX_ANCESTORS: u32 = 10; diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 27015420b..684cec58c 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -22,12 +22,18 @@ impl Build { crate_config.as_example(self.build.example.unwrap()); } - if self.build.platform.is_some() && self.build.platform.unwrap().to_uppercase() == "DESKTOP" { - crate::builder::build_desktop(&crate_config)?; + match self.build.platform.as_str() { + "web" => { + crate::builder::build(&crate_config)?; + } + "desktop" => { + crate::builder::build_desktop(&crate_config)?; + } + _ => { + return Err(anyhow::anyhow!("Unsoppurt platform target.")); + } } - crate::builder::build(&crate_config)?; - let temp = gen_page(&crate_config.dioxus_config, false); let mut file = std::fs::File::create( diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 454da71fe..ece00e07e 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -20,8 +20,8 @@ pub struct ConfigOptsBuild { pub example: Option, /// Build platform: support Web & Desktop [default: "web"] - #[structopt(long)] - pub platform: Option, + #[structopt(long, default_value = "web")] + pub platform: String, } #[derive(Clone, Debug, Default, Deserialize, StructOpt)] @@ -40,8 +40,8 @@ pub struct ConfigOptsServe { pub release: bool, /// Build platform: support Web & Desktop [default: "web"] - #[structopt(long)] - pub platform: Option, + #[structopt(long, default_value = "web")] + pub platform: String, } /// Ensure the given value for `--public-url` is formatted correctly. diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index a84d8136d..fd8f3c4f5 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -21,25 +21,38 @@ impl Serve { crate_config.as_example(self.serve.example.unwrap()); } - if self.serve.platform.is_some() && self.serve.platform.unwrap().to_uppercase() == "DESKTOP" { - crate::builder::build_desktop(&crate_config)?; - - match &crate_config.executable { - crate::ExecutableType::Binary(name) - | crate::ExecutableType::Lib(name) - | crate::ExecutableType::Example(name) => { - let mut file = crate_config.out_dir.join(name); - if cfg!(windows) { - file.set_extension("exe"); - } - Command::new(crate_config.out_dir.join(file).to_str().unwrap().to_string()) - .output()?; - } + match self.serve.platform.as_str() { + "web" => { + crate::builder::build(&crate_config)?; } - return Ok(()); - } + "desktop" => { + crate::builder::build_desktop(&crate_config)?; - crate::builder::build(&crate_config).expect("build failed"); + match &crate_config.executable { + crate::ExecutableType::Binary(name) + | crate::ExecutableType::Lib(name) + | crate::ExecutableType::Example(name) => { + let mut file = crate_config.out_dir.join(name); + if cfg!(windows) { + file.set_extension("exe"); + } + Command::new( + crate_config + .out_dir + .join(file) + .to_str() + .unwrap() + .to_string(), + ) + .output()?; + } + } + return Ok(()); + } + _ => { + return Err(anyhow::anyhow!("Unsoppurt platform target.")); + } + } // generate dev-index page Serve::regen_dev_page(&crate_config)?; diff --git a/src/config.rs b/src/config.rs index fa7e1bd1c..088955910 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,9 +13,8 @@ impl DioxusConfig { let crate_dir = crate::cargo::crate_root()?; if !crate_dir.join("Dioxus.toml").is_file() { - return Err(crate::error::Error::Unique( - "Config file: `Dioxus.toml` not found.".into(), - )); + log::warn!("Config file: `Dioxus.toml` not found; using default config."); + return Ok(DioxusConfig::default()); } let mut dioxus_conf_file = File::open(crate_dir.join("Dioxus.toml"))?; @@ -151,12 +150,7 @@ impl CrateConfig { .lib .as_ref() .and_then(|lib| lib.name.clone()) - .or_else(|| { - manifest - .package - .as_ref().map(|pkg| pkg.name.clone()) - - }) + .or_else(|| manifest.package.as_ref().map(|pkg| pkg.name.clone())) .expect("No lib found from cargo metadata"); let executable = ExecutableType::Binary(output_filename); diff --git a/src/main.rs b/src/main.rs index 1ef3a08f1..6b8c177db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,6 @@ async fn main() -> Result<()> { } } - // Commands::Config(_) => { - // // - // } Commands::Serve(opts) => { if let Err(e) = opts.serve().await { log::error!("serve error: {}", e); diff --git a/src/server/mod.rs b/src/server/mod.rs index 364b7c572..c7c9074ed 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -58,16 +58,16 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { | DebouncedEvent::Write(e) | DebouncedEvent::Remove(e) | DebouncedEvent::Rename(e, _) => { - let mut reload = false; + let mut should_reload = false; for path in &allow_watch_path { let temp = crate_dir.clone().join(path); if e.starts_with(temp) { - reload = true; + should_reload = true; break; } } - if reload && builder::build(&watcher_conf).is_ok() { + if should_reload && builder::build(&watcher_conf).is_ok() { // change the websocket reload state to true; // the page will auto-reload. if watcher_conf From 2c0b0460c9da1f8b30e3c91cce070e669b23d908 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 25 Jan 2022 18:01:33 +0800 Subject: [PATCH 038/516] fix: error wrap --- src/config.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/config.rs b/src/config.rs index 088955910..1151445ef 100644 --- a/src/config.rs +++ b/src/config.rs @@ -21,15 +21,8 @@ impl DioxusConfig { let mut meta_str = String::new(); dioxus_conf_file.read_to_string(&mut meta_str)?; - match toml::from_str::(&meta_str) { - Ok(v) => Ok(v), - Err(e) => { - log::error!("{}", e); - Err(crate::error::Error::Unique( - "Dioxus.toml parse failed".into(), - )) - } - } + toml::from_str::(&meta_str) + .map_err(|_| crate::Error::Unique("Dioxus.toml parse failed".into())) } } From 20977a49f0e0329dcdc77ccd01f4d5bb83fcf989 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Wed, 26 Jan 2022 00:21:40 +0800 Subject: [PATCH 039/516] fix: high CPU usage --- src/assets/autoreload.js | 17 ++++------- src/server/mod.rs | 63 ++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/assets/autoreload.js b/src/assets/autoreload.js index ff4444085..b2baa1907 100644 --- a/src/assets/autoreload.js +++ b/src/assets/autoreload.js @@ -5,21 +5,16 @@ 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) => { + ws.addEventListener("message", (ev) => { if (ev.data == "reload") { window.location.reload(); } - }; + }); + + ws.addEventListener("open", () => { + ws.send("init"); + }); - ws.onclose = reload_upon_connect; })(); \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index c7c9074ed..24504498a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -20,8 +20,8 @@ struct WsRelodState { } impl WsRelodState { - fn change(&mut self) { - self.update = !self.update; + fn change(&mut self, state: bool) { + self.update = state; } } @@ -34,40 +34,35 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { // file watcher: check file change let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); - watcher - .watch(config.crate_dir.clone(), notify::RecursiveMode::Recursive) - .unwrap(); + let allow_watch_path = config + .dioxus_config + .web + .watcher + .watch_path + .clone() + .unwrap_or_else(|| vec![PathBuf::from("src")]); + for sub_path in allow_watch_path { + watcher + .watch( + config.crate_dir.join(sub_path), + notify::RecursiveMode::Recursive, + ) + .unwrap(); + } let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false })); let watcher_conf = config.clone(); let watcher_ws_state = ws_reload_state.clone(); tokio::spawn(async move { - let allow_watch_path = watcher_conf - .dioxus_config - .web - .watcher - .watch_path - .clone() - .unwrap_or_else(|| vec![PathBuf::from("src")]); - let crate_dir = watcher_conf.crate_dir.clone(); loop { if let Ok(v) = rx.recv() { match v { - DebouncedEvent::Create(e) - | DebouncedEvent::Write(e) - | DebouncedEvent::Remove(e) - | DebouncedEvent::Rename(e, _) => { - let mut should_reload = false; - for path in &allow_watch_path { - let temp = crate_dir.clone().join(path); - if e.starts_with(temp) { - should_reload = true; - break; - } - } - - if should_reload && builder::build(&watcher_conf).is_ok() { + DebouncedEvent::Create(_) + | DebouncedEvent::Write(_) + | DebouncedEvent::Remove(_) + | DebouncedEvent::Rename(_, _) => { + if builder::build(&watcher_conf).is_ok() { // change the websocket reload state to true; // the page will auto-reload. if watcher_conf @@ -79,7 +74,7 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { { let _ = Serve::regen_dev_page(&watcher_conf); } - watcher_ws_state.lock().unwrap().change(); + watcher_ws_state.lock().unwrap().change(true); } } _ => {} @@ -118,13 +113,17 @@ async fn ws_handler( ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { loop { - tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; if state.lock().unwrap().update { - socket + // ignore the error + if socket .send(Message::Text(String::from("reload"))) .await - .unwrap(); - state.lock().unwrap().change(); + .is_err() + { + return; + } + state.lock().unwrap().change(false); } } }) From 78980b2dbf62278bf939ece72c67f9ab35a39807 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Wed, 26 Jan 2022 10:18:31 +0800 Subject: [PATCH 040/516] fix: use channel --- src/assets/autoreload.js | 4 ++++ src/server/mod.rs | 52 +++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/assets/autoreload.js b/src/assets/autoreload.js index b2baa1907..89054664e 100644 --- a/src/assets/autoreload.js +++ b/src/assets/autoreload.js @@ -17,4 +17,8 @@ ws.send("init"); }); + ws.addEventListener("close", () => { + alert("Dev-Server Socket Closed."); + }); + })(); \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index 24504498a..366ea41ab 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -9,31 +9,26 @@ use notify::{watcher, DebouncedEvent, Watcher}; use std::time::Duration; use std::{ path::PathBuf, - sync::{mpsc::channel, Arc, Mutex}, + sync::{mpsc::channel, Arc}, }; use tower_http::services::ServeDir; use crate::{builder, serve::Serve, CrateConfig}; +use tokio::sync::broadcast; struct WsRelodState { - update: bool, -} - -impl WsRelodState { - fn change(&mut self, state: bool) { - self.update = state; - } + update: broadcast::Sender, } pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { log::info!("🚀 Starting development server..."); - let (tx, rx) = channel(); + let (watcher_tx, watcher_rx) = channel(); let dist_path = config.out_dir.clone(); // file watcher: check file change - let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); + let mut watcher = watcher(watcher_tx, Duration::from_secs(1)).unwrap(); let allow_watch_path = config .dioxus_config .web @@ -50,13 +45,14 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { .unwrap(); } - let ws_reload_state = Arc::new(Mutex::new(WsRelodState { update: false })); + let (reload_tx, _) = broadcast::channel(100); + let ws_reload_state = Arc::new(WsRelodState { update: reload_tx }); let watcher_conf = config.clone(); - let watcher_ws_state = ws_reload_state.clone(); + let watcher_ws_state = ws_reload_state.update.clone(); tokio::spawn(async move { loop { - if let Ok(v) = rx.recv() { + if let Ok(v) = watcher_rx.recv() { match v { DebouncedEvent::Create(_) | DebouncedEvent::Write(_) @@ -74,7 +70,8 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { { let _ = Serve::regen_dev_page(&watcher_conf); } - watcher_ws_state.lock().unwrap().change(true); + println!("send reload!"); + watcher_ws_state.send("reload".into()).unwrap(); } } _ => {} @@ -109,21 +106,26 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { async fn ws_handler( ws: WebSocketUpgrade, _: Option>, - Extension(state): Extension>>, + Extension(state): Extension>, ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { + let mut rx = state.update.subscribe(); loop { - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - if state.lock().unwrap().update { - // ignore the error - if socket - .send(Message::Text(String::from("reload"))) - .await - .is_err() - { - return; + println!("wait"); + if let Ok(v) = rx.recv().await { + println!("trigger recv: {}", v); + if v == "reload" { + // ignore the error + if socket + .send(Message::Text(String::from("reload"))) + .await + .is_err() + { + return; + } } - state.lock().unwrap().change(false); + } else { + println!("SS"); } } }) From 7aeddba4270c404a1898382acd0b89b4b33096db Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 27 Jan 2022 01:03:17 +0800 Subject: [PATCH 041/516] fix: auto-connect `ws` server --- src/assets/autoreload.js | 40 +++++++------- src/server/mod.rs | 111 ++++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 74 deletions(-) diff --git a/src/assets/autoreload.js b/src/assets/autoreload.js index 89054664e..a6bbac5da 100644 --- a/src/assets/autoreload.js +++ b/src/assets/autoreload.js @@ -1,24 +1,22 @@ (function () { - var protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; - - var url = protocol + "//" + window.location.host + "/_dioxus/ws"; - - var poll_interval = 2000; + var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + var url = protocol + '//' + window.location.host + '/_dioxus/ws'; + var poll_interval = 8080; + 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.addEventListener("message", (ev) => { - if (ev.data == "reload") { - window.location.reload(); - } - }); - - ws.addEventListener("open", () => { - ws.send("init"); - }); - - ws.addEventListener("close", () => { - alert("Dev-Server Socket Closed."); - }); - -})(); \ No newline at end of file + ws.onmessage = (ev) => { + if (ev.data == "reload") { + window.location.reload(); + } + }; + ws.onclose = reload_upon_connect; +})() \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index 366ea41ab..7dd4151eb 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -36,6 +36,7 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { .watch_path .clone() .unwrap_or_else(|| vec![PathBuf::from("src")]); + for sub_path in allow_watch_path { watcher .watch( @@ -46,60 +47,63 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { } let (reload_tx, _) = broadcast::channel(100); - let ws_reload_state = Arc::new(WsRelodState { update: reload_tx }); + + let ws_reload_state = Arc::new(WsRelodState { + update: reload_tx.clone(), + }); let watcher_conf = config.clone(); - let watcher_ws_state = ws_reload_state.update.clone(); + tokio::spawn(async move { - loop { - if let Ok(v) = watcher_rx.recv() { - match v { - DebouncedEvent::Create(_) - | DebouncedEvent::Write(_) - | DebouncedEvent::Remove(_) - | DebouncedEvent::Rename(_, _) => { - if builder::build(&watcher_conf).is_ok() { - // change the websocket reload state to true; - // the page will auto-reload. - if watcher_conf - .dioxus_config - .web - .watcher - .reload_html - .unwrap_or(false) - { - let _ = Serve::regen_dev_page(&watcher_conf); - } - println!("send reload!"); - watcher_ws_state.send("reload".into()).unwrap(); + while let Ok(v) = watcher_rx.recv() { + match v { + DebouncedEvent::Create(_) + | DebouncedEvent::Write(_) + | DebouncedEvent::Remove(_) + | DebouncedEvent::Rename(_, _) => { + if builder::build(&watcher_conf).is_ok() { + // change the websocket reload state to true; + // the page will auto-reload. + if watcher_conf + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&watcher_conf); } + reload_tx.send("reload".into()).unwrap(); } - _ => {} } + _ => {} } } }); - let app = Router::new() - .route("/_dioxus/ws", get(ws_handler)) - .fallback( - get_service(ServeDir::new(config.crate_dir.join(&dist_path))).handle_error( - |error: std::io::Error| async move { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), - ) - }, - ), - ) - .layer(AddExtensionLayer::new(ws_reload_state.clone())); - // start serve dev-server at 0.0.0.0:8080 let port = "8080"; log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port); + axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) - .serve(app.into_make_service()) + .serve( + Router::new() + .route("/_dioxus/ws", get(ws_handler)) + .fallback( + get_service(ServeDir::new(config.crate_dir.join(&dist_path))).handle_error( + |error: std::io::Error| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }, + ), + ) + .layer(AddExtensionLayer::new(ws_reload_state)) + .into_make_service(), + ) .await?; + Ok(()) } @@ -109,23 +113,24 @@ async fn ws_handler( Extension(state): Extension>, ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { + + log::info!("New websocket conenct."); + let mut rx = state.update.subscribe(); + loop { - println!("wait"); - if let Ok(v) = rx.recv().await { - println!("trigger recv: {}", v); - if v == "reload" { - // ignore the error - if socket - .send(Message::Text(String::from("reload"))) - .await - .is_err() - { - return; - } + let v = rx.recv().await.unwrap(); + println!("trigger recv: {}", v); + if v == "reload" { + // ignore the error + if socket + .send(Message::Text(String::from("reload"))) + .await + .is_err() + { + println!("send failed"); + return; } - } else { - println!("SS"); } } }) From b6172998609812a02cc9e68786e0cd283bc0e685 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 27 Jan 2022 11:13:41 +0800 Subject: [PATCH 042/516] fix: auto-reload --- Cargo.toml | 3 +- src/assets/autoreload.js | 1 + src/server/mod.rs | 131 +++++++++++++++++++++------------------ 3 files changed, 73 insertions(+), 62 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68dd3ee56..5f6443245 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ toml = "0.5.8" fs_extra = "1.2.0" cargo_toml = "0.10.0" futures = "0.3.12" -notify = "4.0.17" +# notify = "4.0.17" +notify = { version = "5.0.0-pre.13", features = ["serde"] } html_parser = "0.6.2" binary-install = "0.0.2" convert_case = "0.5.0" diff --git a/src/assets/autoreload.js b/src/assets/autoreload.js index a6bbac5da..da62e5eae 100644 --- a/src/assets/autoreload.js +++ b/src/assets/autoreload.js @@ -15,6 +15,7 @@ var ws = new WebSocket(url); ws.onmessage = (ev) => { if (ev.data == "reload") { + // alert("reload!!!"); window.location.reload(); } }; diff --git a/src/server/mod.rs b/src/server/mod.rs index 7dd4151eb..45d7191d0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -5,12 +5,9 @@ use axum::{ routing::{get, get_service}, AddExtensionLayer, Router, }; -use notify::{watcher, DebouncedEvent, Watcher}; -use std::time::Duration; -use std::{ - path::PathBuf, - sync::{mpsc::channel, Arc}, -}; +use notify::{RecommendedWatcher, Watcher}; + +use std::{path::PathBuf, sync::Arc}; use tower_http::services::ServeDir; use crate::{builder, serve::Serve, CrateConfig}; @@ -23,12 +20,33 @@ struct WsRelodState { pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { log::info!("🚀 Starting development server..."); - let (watcher_tx, watcher_rx) = channel(); - let dist_path = config.out_dir.clone(); + let (reload_tx, _) = broadcast::channel(100); + + let ws_reload_state = Arc::new(WsRelodState { + update: reload_tx.clone(), + }); + // file watcher: check file change - let mut watcher = watcher(watcher_tx, Duration::from_secs(1)).unwrap(); + let watcher_conf = config.clone(); + let mut watcher = RecommendedWatcher::new(move |_| { + if builder::build(&watcher_conf).is_ok() { + // change the websocket reload state to true; + // the page will auto-reload. + if watcher_conf + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&watcher_conf); + } + reload_tx.send("reload".into()).unwrap(); + } + }) + .unwrap(); let allow_watch_path = config .dioxus_config .web @@ -40,46 +58,38 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { for sub_path in allow_watch_path { watcher .watch( - config.crate_dir.join(sub_path), + &config.crate_dir.join(sub_path), notify::RecursiveMode::Recursive, ) .unwrap(); } - let (reload_tx, _) = broadcast::channel(100); - - let ws_reload_state = Arc::new(WsRelodState { - update: reload_tx.clone(), - }); - - let watcher_conf = config.clone(); - - tokio::spawn(async move { - while let Ok(v) = watcher_rx.recv() { - match v { - DebouncedEvent::Create(_) - | DebouncedEvent::Write(_) - | DebouncedEvent::Remove(_) - | DebouncedEvent::Rename(_, _) => { - if builder::build(&watcher_conf).is_ok() { - // change the websocket reload state to true; - // the page will auto-reload. - if watcher_conf - .dioxus_config - .web - .watcher - .reload_html - .unwrap_or(false) - { - let _ = Serve::regen_dev_page(&watcher_conf); - } - reload_tx.send("reload".into()).unwrap(); - } - } - _ => {} - } - } - }); + // tokio::spawn(async move { + // while let Ok(v) = watcher_rx.recv() { + // match v { + // DebouncedEvent::Create(_) + // | DebouncedEvent::Write(_) + // | DebouncedEvent::Remove(_) + // | DebouncedEvent::Rename(_, _) => { + // if builder::build(&watcher_conf).is_ok() { + // // change the websocket reload state to true; + // // the page will auto-reload. + // if watcher_conf + // .dioxus_config + // .web + // .watcher + // .reload_html + // .unwrap_or(false) + // { + // let _ = Serve::regen_dev_page(&watcher_conf); + // } + // reload_tx.send("reload".into()).unwrap(); + // } + // } + // _ => {} + // } + // } + // }); // start serve dev-server at 0.0.0.0:8080 let port = "8080"; @@ -113,25 +123,24 @@ async fn ws_handler( Extension(state): Extension>, ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { - - log::info!("New websocket conenct."); - let mut rx = state.update.subscribe(); - - loop { - let v = rx.recv().await.unwrap(); - println!("trigger recv: {}", v); - if v == "reload" { - // ignore the error - if socket - .send(Message::Text(String::from("reload"))) - .await - .is_err() - { - println!("send failed"); - return; + let reload_watcher = tokio::spawn(async move { + loop { + let v = rx.recv().await.unwrap(); + println!("trigger recv: {}", v); + if v == "reload" { + // ignore the error + if socket + .send(Message::Text(String::from("reload"))) + .await + .is_err() + { + break; + } } } - } + }); + + reload_watcher.await.unwrap(); }) } From b8b0303904b3f01da5942355b5c61a5ea33c325c Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 27 Jan 2022 12:09:54 +0800 Subject: [PATCH 043/516] fix: dev server `auto-reload` --- Cargo.toml | 2 +- src/server/mod.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f6443245..5bbfd7ac2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ toml = "0.5.8" fs_extra = "1.2.0" cargo_toml = "0.10.0" futures = "0.3.12" -# notify = "4.0.17" notify = { version = "5.0.0-pre.13", features = ["serde"] } html_parser = "0.6.2" binary-install = "0.0.2" @@ -30,6 +29,7 @@ cargo_metadata = "0.14.1" tokio = { version = "1.15.0", features = ["full"] } atty = "0.2.14" regex = "1.5.4" +chrono = "0.4.19" axum = { version = "0.4.4", features = ["ws", "headers"] } tower-http = { version = "0.2.0", features = ["fs", "trace"] } diff --git a/src/server/mod.rs b/src/server/mod.rs index 45d7191d0..b1684d980 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -28,10 +28,14 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { update: reload_tx.clone(), }); + let mut last_update_time = chrono::Local::now().timestamp(); + // file watcher: check file change let watcher_conf = config.clone(); - let mut watcher = RecommendedWatcher::new(move |_| { - if builder::build(&watcher_conf).is_ok() { + let mut watcher = RecommendedWatcher::new(move | _ : notify::Result| { + if chrono::Local::now().timestamp() > last_update_time + && builder::build(&watcher_conf).is_ok() + { // change the websocket reload state to true; // the page will auto-reload. if watcher_conf @@ -43,7 +47,9 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { { let _ = Serve::regen_dev_page(&watcher_conf); } + println!("watcher send reload"); reload_tx.send("reload".into()).unwrap(); + last_update_time = chrono::Local::now().timestamp(); } }) .unwrap(); @@ -127,7 +133,6 @@ async fn ws_handler( let reload_watcher = tokio::spawn(async move { loop { let v = rx.recv().await.unwrap(); - println!("trigger recv: {}", v); if v == "reload" { // ignore the error if socket From 23e09f0d9f0b3740cb9b9809e3efdd0f2652c21c Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 27 Jan 2022 14:11:18 +0800 Subject: [PATCH 044/516] fix: delete comment --- src/server/mod.rs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index b1684d980..880f25ceb 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -32,7 +32,7 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { // file watcher: check file change let watcher_conf = config.clone(); - let mut watcher = RecommendedWatcher::new(move | _ : notify::Result| { + let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { if chrono::Local::now().timestamp() > last_update_time && builder::build(&watcher_conf).is_ok() { @@ -70,33 +70,6 @@ pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { .unwrap(); } - // tokio::spawn(async move { - // while let Ok(v) = watcher_rx.recv() { - // match v { - // DebouncedEvent::Create(_) - // | DebouncedEvent::Write(_) - // | DebouncedEvent::Remove(_) - // | DebouncedEvent::Rename(_, _) => { - // if builder::build(&watcher_conf).is_ok() { - // // change the websocket reload state to true; - // // the page will auto-reload. - // if watcher_conf - // .dioxus_config - // .web - // .watcher - // .reload_html - // .unwrap_or(false) - // { - // let _ = Serve::regen_dev_page(&watcher_conf); - // } - // reload_tx.send("reload".into()).unwrap(); - // } - // } - // _ => {} - // } - // } - // }); - // start serve dev-server at 0.0.0.0:8080 let port = "8080"; log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port); From b23e69cfbbf0af2d96ab897bf45e3944f863be59 Mon Sep 17 00:00:00 2001 From: c5soft Date: Thu, 27 Jan 2022 15:54:49 +0800 Subject: [PATCH 045/516] Refactor extension, make it easy to maintain and unique to register --- extension/LICENSE.txt | 21 +++++++++++ extension/package.json | 15 ++++---- extension/src/extension.ts | 75 +++++++++++++------------------------- 3 files changed, 54 insertions(+), 57 deletions(-) create mode 100644 extension/LICENSE.txt diff --git a/extension/LICENSE.txt b/extension/LICENSE.txt new file mode 100644 index 000000000..6fd3d45f6 --- /dev/null +++ b/extension/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 DioxusLabs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/extension/package.json b/extension/package.json index 3b72cfa8c..0a3d50b67 100644 --- a/extension/package.json +++ b/extension/package.json @@ -17,19 +17,19 @@ "Other" ], "activationEvents": [ - "onCommand:extension.htmlToRsx", - "onCommand:extension.htmlToComponent" + "onCommand:extension.htmlToDioxusRsx", + "onCommand:extension.htmlToDioxusComponent" ], "main": "./out/extension", "contributes": { "commands": [ { - "command": "extension.htmlToRsx", - "title": "Convert HTML to RSX" + "command": "extension.htmlToDioxusRsx", + "title": "Dioxus: Convert HTML to RSX" }, { - "command": "extension.htmlToComponent", - "title": "Convert HTML to Dioxus Component" + "command": "extension.htmlToDioxusComponent", + "title": "Dioxus: Convert HTML to Component" } ] }, @@ -37,7 +37,8 @@ "vscode:prepublish": "npm run compile", "compile": "tsc -p ./", "lint": "eslint . --ext .ts,.tsx", - "watch": "tsc -watch -p ./" + "watch": "tsc -watch -p ./", + "package": "vsce package" }, "devDependencies": { "@types/node": "^12.12.0", diff --git a/extension/src/extension.ts b/extension/src/extension.ts index c9ab06662..5a41c7f5f 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -1,57 +1,32 @@ -'use strict'; - import * as vscode from 'vscode'; import { spawn } from "child_process"; export function activate(context: vscode.ExtensionContext) { - const htmlToPureRsx = vscode.commands.registerCommand('extension.htmlToRsx', function () { - // Get the active text editor - const editor = vscode.window.activeTextEditor; - - if (editor) { - const document = editor.document; - const selection = editor.selection; - const word = document.getText(selection); - - const child_proc = spawn("dioxus", ["translate", "--source", word]); - - let result = ''; - child_proc.stdout?.on('data', data => result += data); - - child_proc.on('close', () => { - editor.edit(editBuilder => { - if (result != '') { - editBuilder.replace(selection, result) - } - }) - }); + function registerCommand(cmd: string) { + function convert(cmd: string) { + const editor = vscode.window.activeTextEditor;// Get the active text editor + if (editor) { + const html = editor.document.getText(editor.selection); + if (html.length > 0) { + let params = ["translate"]; + if (cmd.includes("Component")) params.push("--component"); + params.push("--source"); + params.push(html); + const child_proc = spawn("dioxus", params); + let result = ''; + child_proc.stdout?.on('data', data => result += data); + child_proc.on('close', () => { + if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); + }); + } else { + vscode.window.showWarningMessage("Please select HTML fragment before invoking this command!"); + } + } } - }); + const handle = vscode.commands.registerCommand(cmd, () => convert(cmd)); + context.subscriptions.push(handle); + } - const htmlToComponent = vscode.commands.registerCommand('extension.htmlToComponent', function () { - // Get the active text editor - const editor = vscode.window.activeTextEditor; - - if (editor) { - const document = editor.document; - const selection = editor.selection; - const word = document.getText(selection); - - const child_proc = spawn("dioxus", ["translate", "--component", "--source", word]); - - let result = ''; - child_proc.stdout?.on('data', data => result += data); - - child_proc.on('close', () => { - editor.edit(editBuilder => { - if (result != '') { - editBuilder.replace(selection, result) - } - }) - }); - } - }); - - context.subscriptions.push(htmlToPureRsx); - context.subscriptions.push(htmlToComponent); + registerCommand('extension.htmlToDioxusRsx'); + registerCommand('extension.htmlToDioxusComponent'); } From 5d9a58b0faae85375b523ea3fa70ce29d58b141c Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 27 Jan 2022 23:06:09 +0800 Subject: [PATCH 046/516] fix: desktop build --- src/builder.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index 583c9bc8f..257072a6a 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -141,6 +141,10 @@ pub fn build_desktop(config: &CrateConfig) -> Result<()> { let output = cmd.output()?; + if !output.status.success() { + return Err(Error::BuildFailed("Program build failed.".into())); + } + if output.status.success() { if config.out_dir.is_dir() { remove_dir_all(&config.out_dir)?; From efe956a1bfd097a2190a7db95286dafe1a2c09d9 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 27 Jan 2022 23:56:44 +0800 Subject: [PATCH 047/516] fix: display log --- src/cli/serve/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index fd8f3c4f5..8f26e0f30 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,5 +1,5 @@ use crate::{cfg::ConfigOptsServe, gen_page, server, CrateConfig}; -use std::{io::Write, path::PathBuf, process::Command}; +use std::{io::Write, path::PathBuf, process::{Command, Stdio}}; use structopt::StructOpt; /// Run the WASM project on dev-server @@ -44,6 +44,7 @@ impl Serve { .unwrap() .to_string(), ) + .stdout(Stdio::inherit()) .output()?; } } From a4ae4598fbf4aa41562efe2c0afbf37049da7ba8 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sat, 5 Feb 2022 09:11:44 +0800 Subject: [PATCH 048/516] docs: update reamde --- README.md | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 75e05b3cf..7bc1fa131 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,46 @@

📦✨ dioxus-cli

-

- Tooling to supercharge dioxus projects -

+

Tooling to supercharge dioxus projects

-![Dioxus Logo](https://dioxuslabs.com/guide/images/dioxuslogo_full.png) - - -# About - dioxus-cli (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. \ No newline at end of file +Now, `dioxus` is in your path, you can use `dioxus --help` to get the help document. + +``` +dioxus 0.1.2 +Build, bundle, & ship your Dioxus app + +USAGE: + dioxus.exe [FLAGS] + +FLAGS: + -h, --help Prints help information + -v Enable verbose logging + -V, --version Prints version information + +SUBCOMMANDS: + build Build the Rust WASM app and all of its assets + clean Clean output artifacts + config Dioxus config file controls + create Init a new project for Dioxus + help Prints this message or the help of the given subcommand(s) + serve Build, watch & serve the Rust WASM app and all of its assets + translate Translate some source file into Dioxus code +``` + +## Get Started + +You can use the `dioxus crate project_name` to init a dioxus project, its will be clone from the [dioxus-template](https://github.com/DioxusLabs/dioxus-template). + +or you can assign the template path: + +``` +dioxus create hello --template gh:dioxuslabs/dioxus-template +``` \ No newline at end of file From 025efc912075348bb90fbaed1cde3e5c61c8ccf9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 6 Feb 2022 16:28:17 -0500 Subject: [PATCH 049/516] fix: translation service --- src/cli/translate/extract_svgs.rs | 1 - src/cli/translate/mod.rs | 240 ++++++++++++++++++++++++------ src/cli/translate/to_component.rs | 183 ----------------------- src/cli/translate/translate.rs | 1 - 4 files changed, 192 insertions(+), 233 deletions(-) delete mode 100644 src/cli/translate/extract_svgs.rs delete mode 100644 src/cli/translate/to_component.rs delete mode 100644 src/cli/translate/translate.rs diff --git a/src/cli/translate/extract_svgs.rs b/src/cli/translate/extract_svgs.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/cli/translate/extract_svgs.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 81b5092ed..a717d632a 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -1,15 +1,14 @@ +use anyhow::Result; use html_parser::Dom; +use html_parser::Element; use html_parser::Node; -use std::fmt::Write; +#[allow(unused_imports)] +use std::fmt::{Display, Formatter}; use std::io::Read; use std::path::PathBuf; use std::process::exit; use structopt::StructOpt; -pub mod extract_svgs; -pub mod to_component; -pub mod translate; - /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, StructOpt)] #[structopt(name = "translate")] @@ -21,11 +20,7 @@ pub struct Translate { /// Input file #[structopt(short, long)] - pub source: Option, - - /// Input file - #[structopt(short, long, parse(from_os_str))] - pub input: Option, + pub file: Option, /// Output file, stdout if not present #[structopt(parse(from_os_str))] @@ -35,23 +30,17 @@ pub struct Translate { impl Translate { pub fn translate(self) -> anyhow::Result<()> { let Translate { - component: as_component, - input, + component, output, - source, + file, } = self; - let contents; - let temp = input.map(|f| { - std::fs::read_to_string(&f).unwrap_or_else(|e| { + let contents = match file { + Some(input) => std::fs::read_to_string(&input).unwrap_or_else(|e| { log::error!("Cloud not read input file: {}.", e); exit(0); - }) - }); - if let Some(..) = temp { - if let Some(s) = source { - contents = s; - } else { + }), + None => { if atty::is(atty::Stream::Stdin) { log::error!("No input file, source, or stdin to translate from."); exit(0); @@ -60,28 +49,21 @@ impl Translate { let mut buffer = String::new(); std::io::stdin().read_to_string(&mut buffer).unwrap(); - contents = buffer.trim().to_string(); + buffer.trim().to_string() } - } else { - contents = temp.unwrap(); - } + }; - // 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, "}}")?; - } + let out_buf = match component { + true => format!("{}", convert_html_to_component(&contents).unwrap()), + false => { + let mut buf = String::new(); + let dom = Dom::parse(&contents).unwrap(); + for child in &dom.children { + simple_render_child(&mut buf, child, 0)?; + } + buf + } + }; if let Some(output) = output { std::fs::write(&output, out_buf)?; @@ -93,7 +75,8 @@ impl Translate { } } -fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { +/// render without handling svgs or anything +fn simple_render_child(f: &mut impl std::fmt::Write, child: &Node, il: u32) -> std::fmt::Result { write_tabs(f, il)?; match child { Node::Text(t) => writeln!(f, "\"{}\"", t)?, @@ -131,9 +114,8 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { None => writeln!(f, "{}: \"\",", new_name)?, } } else { - match name.as_str() { - "for" | "async" | "type" | "as" => write!(f, "r#")?, - _ => {} + if matches!(name.as_str(), "for" | "async" | "type" | "as") { + write!(f, "r#")? } match value { @@ -145,7 +127,7 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { // now the children for child in &el.children { - render_child(f, child, il + 1)?; + simple_render_child(f, child, il + 1)?; } // close the tag @@ -156,9 +138,171 @@ fn render_child(f: &mut impl Write, child: &Node, il: u32) -> std::fmt::Result { Ok(()) } -fn write_tabs(f: &mut impl Write, num: u32) -> std::fmt::Result { +pub fn convert_html_to_component(html: &str) -> Result { + Ok(ComponentRenderer { + dom: Dom::parse(html)?, + icon_index: 0, + }) +} + +#[allow(dead_code)] +pub struct ComponentRenderer { + dom: Dom, + icon_index: usize, +} + +impl Display for ComponentRenderer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + r##" +fn component(cx: Scope) -> Element {{ + cx.render(rsx!("## + )?; + let mut svg_nodes = vec![]; + + let mut svg_idx = 0; + for child in &self.dom.children { + render_child(f, child, 2, &mut svg_nodes, true, &mut svg_idx)?; + } + write!( + f, + r##" )) +}}"## + )?; + + if svg_idx == 0 { + return Ok(()); + } + + writeln!(f, "\n\nmod icons {{")?; + + let mut id = 0; + while let Some(svg) = svg_nodes.pop() { + writeln!( + f, + r##" pub(super) fn icon_{}(cx: Scope) -> Element {{ + cx.render(rsx!("##, + id + )?; + write_tabs(f, 3)?; + + render_element(f, svg, 3, &mut svg_nodes, false, &mut 0)?; + writeln!(f, "\t\t))\n\t}}\n")?; + id += 1; + } + + writeln!(f, "}}")?; + + Ok(()) + } +} + +fn render_child<'a>( + f: &mut Formatter<'_>, + child: &'a Node, + il: u32, + svg_buffer: &mut Vec<&'a Element>, + skip_svg: bool, + svg_idx: &mut usize, +) -> std::fmt::Result { + write_tabs(f, il)?; + match child { + Node::Text(t) => writeln!(f, "\"{}\"", t)?, + Node::Comment(e) => writeln!(f, "/* {} */", e)?, + Node::Element(el) => render_element(f, el, il, svg_buffer, skip_svg, svg_idx)?, + }; + Ok(()) +} + +fn render_element<'a>( + f: &mut Formatter<'_>, + el: &'a Element, + il: u32, + svg_buffer: &mut Vec<&'a Element>, + skip_svg: bool, + svg_idx: &mut usize, +) -> std::fmt::Result { + if el.name == "svg" && skip_svg { + svg_buffer.push(el); + // todo: attach the right icon ID + writeln!(f, "icons::icon_{} {{}}", svg_idx)?; + *svg_idx += 1; + return Ok(()); + } + + // 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, "\",")?; + } + writeln!(f)?; + + // 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 { + if matches!(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, svg_buffer, skip_svg, svg_idx)?; + } + + // close the tag + write_tabs(f, il)?; + writeln!(f, "}}")?; + Ok(()) +} + +fn write_tabs(f: &mut impl std::fmt::Write, num: u32) -> std::fmt::Result { for _ in 0..num { write!(f, " ")? } Ok(()) } + +#[test] +fn generates_svgs() { + use std::io::Write; + + let st = include_str!("../../../tests/svg.html"); + + let out = format!("{:}", convert_html_to_component(st).unwrap()); + dbg!(&out); + + std::fs::File::create("svg_rsx.rs") + .unwrap() + .write_all(out.as_bytes()) + .unwrap(); +} diff --git a/src/cli/translate/to_component.rs b/src/cli/translate/to_component.rs deleted file mode 100644 index c83777ccb..000000000 --- a/src/cli/translate/to_component.rs +++ /dev/null @@ -1,183 +0,0 @@ -//! Intelligently converts html to rsx with appropraite transformations and extractions. -//! -//! - [*] Creates a component -//! - [ ] Extracts svgs -//! - [ ] Attempts to extract lists - -#[allow(unused_imports)] -use std::{ - fmt::{Display, Formatter}, - io::Write, -}; - -use anyhow::Result; - -use html_parser::{Dom, Element, Node}; - -pub fn convert_html_to_component(html: &str) -> Result { - Ok(ComponentRenderer { - dom: Dom::parse(html)?, - icon_index: 0, - }) -} - -#[allow(dead_code)] -pub struct ComponentRenderer { - dom: Dom, - icon_index: usize, -} - -impl Display for ComponentRenderer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!( - f, - r##" -fn component(cx: Scope) -> Element {{ - cx.render(rsx!("## - )?; - let mut svg_nodes = vec![]; - - let mut svg_idx = 0; - for child in &self.dom.children { - render_child(f, child, 2, &mut svg_nodes, true, &mut svg_idx)?; - } - write!( - f, - r##" )) -}}"## - )?; - - if svg_idx == 0 { - return Ok(()); - } - - writeln!(f, "\n\nmod icons {{")?; - - let mut id = 0; - while let Some(svg) = svg_nodes.pop() { - writeln!( - f, - r##" pub(super) fn icon_{}(cx: Scope) -> Element {{ - cx.render(rsx!("##, - id - )?; - write_tabs(f, 3)?; - - render_element(f, svg, 3, &mut svg_nodes, false, &mut 0)?; - writeln!(f, "\t\t))\n\t}}\n")?; - id += 1; - } - - writeln!(f, "}}")?; - - Ok(()) - } -} - -fn render_child<'a>( - f: &mut Formatter<'_>, - child: &'a Node, - il: u32, - svg_buffer: &mut Vec<&'a Element>, - skip_svg: bool, - svg_idx: &mut usize, -) -> std::fmt::Result { - write_tabs(f, il)?; - match child { - Node::Text(t) => writeln!(f, "\"{}\"", t)?, - Node::Comment(e) => writeln!(f, "/* {} */", e)?, - Node::Element(el) => render_element(f, el, il, svg_buffer, skip_svg, svg_idx)?, - }; - Ok(()) -} - -fn render_element<'a>( - f: &mut Formatter<'_>, - el: &'a Element, - il: u32, - svg_buffer: &mut Vec<&'a Element>, - skip_svg: bool, - svg_idx: &mut usize, -) -> std::fmt::Result { - if el.name == "svg" && skip_svg { - svg_buffer.push(el); - // todo: attach the right icon ID - writeln!(f, "icons::icon_{} {{}}", svg_idx)?; - *svg_idx += 1; - return Ok(()); - } - - // 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, "\",")?; - } - writeln!(f)?; - - // 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, svg_buffer, skip_svg, svg_idx)?; - } - - // 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(()) -} - -#[test] -fn generates_svgs() { - let st = include_str!("../../../tests/svg.html"); - - let out = format!("{:}", convert_html_to_component(st).unwrap()); - dbg!(&out); - - std::fs::File::create("svg_rsx.rs") - .unwrap() - .write_all(out.as_bytes()) - .unwrap(); -} diff --git a/src/cli/translate/translate.rs b/src/cli/translate/translate.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/cli/translate/translate.rs +++ /dev/null @@ -1 +0,0 @@ - From eb7388794a09eae788ad70aaabfb483e37cc19b3 Mon Sep 17 00:00:00 2001 From: Aster Date: Mon, 7 Feb 2022 05:28:18 +0800 Subject: [PATCH 050/516] Replace struct-opt with clap --- .gitignore | 1 + Cargo.toml | 26 +++++++++++++------------- README.md | 7 ++++++- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 96ef6c0b9..2e049011e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target Cargo.lock +.idea/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 5bbfd7ac2..0f2ad96c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-cli" -version = "0.1.2" +version = "0.1.3" authors = ["Jonathan Kelley"] edition = "2018" description = "CLI tool for developing, testing, and publishing Dioxus apps" @@ -9,31 +9,31 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -thiserror = "1.0.23" -log = "0.4.13" +thiserror = "1.0.30" +log = "0.4.14" fern = { version = "0.6.0", features = ["colored"] } wasm-bindgen-cli-support = "0.2.79" -anyhow = "1.0.38" -serde = { version = "1.0.133", features = ["derive"] } -serde_json = "1" +anyhow = "1.0.53" +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" toml = "0.5.8" fs_extra = "1.2.0" -cargo_toml = "0.10.0" -futures = "0.3.12" +cargo_toml = "0.11.4" +futures = "0.3.21" notify = { version = "5.0.0-pre.13", features = ["serde"] } html_parser = "0.6.2" binary-install = "0.0.2" convert_case = "0.5.0" -structopt = "0.3.25" cargo_metadata = "0.14.1" -tokio = { version = "1.15.0", features = ["full"] } +tokio = { version = "1.16.1", features = ["full"] } atty = "0.2.14" regex = "1.5.4" chrono = "0.4.19" +clap = "3.0.14" -axum = { version = "0.4.4", features = ["ws", "headers"] } -tower-http = { version = "0.2.0", features = ["fs", "trace"] } -headers = "0.3" +axum = { version = "0.4.5", features = ["ws", "headers"] } +tower-http = { version = "0.2.2", features = ["fs", "trace"] } +headers = "0.3.7" # hyper = { version = "0.14.11", features = ["full"] } [[bin]] diff --git a/README.md b/README.md index 75e05b3cf..ab4cf1225 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,13 @@ dioxus-cli (inspired by wasm-pack and webpack) is a tool to help get dioxus proj ## Installation -``` +```shell +# for stable $ cargo install dioxus-cli +# for latest +$ cargo install --git https://github.com/DioxusLabs/cli +# for developing +$ cargo install --path . --debug ``` Now, `dioxus` is in your path. \ No newline at end of file From b6c6dd6532335c5acd2c2eea54300f6f6b341a64 Mon Sep 17 00:00:00 2001 From: Aster Date: Tue, 8 Feb 2022 00:51:21 +0800 Subject: [PATCH 051/516] Clean struct-opt derive --- Cargo.toml | 7 ++++--- src/cli/build/mod.rs | 8 +++----- src/cli/cfg.rs | 9 +++------ src/cli/clean/mod.rs | 10 ++-------- src/cli/config/mod.rs | 7 ++----- src/cli/create/mod.rs | 13 +------------ src/cli/mod.rs | 33 ++++++++++++++++++++++++++------- src/cli/serve/mod.rs | 6 ++---- src/cli/translate/mod.rs | 13 ++----------- tests/main.rs | 4 ++++ 10 files changed, 49 insertions(+), 61 deletions(-) create mode 100644 tests/main.rs diff --git a/Cargo.toml b/Cargo.toml index 0f2ad96c4..89943ec62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,13 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# cli core +clap = {version = "3.0.14", features = ["derive"]} thiserror = "1.0.30" +wasm-bindgen-cli-support = "0.2.79" +# features log = "0.4.14" fern = { version = "0.6.0", features = ["colored"] } -wasm-bindgen-cli-support = "0.2.79" -anyhow = "1.0.53" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" toml = "0.5.8" @@ -29,7 +31,6 @@ tokio = { version = "1.16.1", features = ["full"] } atty = "0.2.14" regex = "1.5.4" chrono = "0.4.19" -clap = "3.0.14" axum = { version = "0.4.5", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 684cec58c..b4d7f5fe4 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,10 +1,8 @@ -use std::{io::Write, path::PathBuf}; +use super::*; -use crate::{cfg::ConfigOptsBuild, gen_page}; -use structopt::StructOpt; /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Parser)] #[structopt(name = "build")] pub struct Build { #[structopt(flatten)] @@ -15,7 +13,7 @@ impl Build { pub fn build(self) -> anyhow::Result<()> { let mut crate_config = crate::CrateConfig::new()?; - // change the relase state. + // change the release state. crate_config.with_release(self.build.release); if self.build.example.is_some() { diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index ece00e07e..fdd627e85 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -1,10 +1,7 @@ -use std::path::PathBuf; -use structopt::StructOpt; - -use serde::Deserialize; +use super::*; /// Config options for the build system. -#[derive(Clone, Debug, Default, Deserialize, StructOpt)] +#[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsBuild { /// The index HTML file to drive the bundling process [default: index.html] #[structopt(parse(from_os_str))] @@ -24,7 +21,7 @@ pub struct ConfigOptsBuild { pub platform: String, } -#[derive(Clone, Debug, Default, Deserialize, StructOpt)] +#[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsServe { /// The index HTML file to drive the bundling process [default: index.html] #[structopt(parse(from_os_str))] diff --git a/src/cli/clean/mod.rs b/src/cli/clean/mod.rs index 62593b1ee..334f93572 100644 --- a/src/cli/clean/mod.rs +++ b/src/cli/clean/mod.rs @@ -1,13 +1,7 @@ -use std::{ - fs::remove_dir_all, - path::PathBuf, - process::{Command, Stdio}, -}; - -use structopt::StructOpt; +use super::*; /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Parser)] #[structopt(name = "clean")] pub struct Clean {} diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index d5291a7d6..e5d7bdbe4 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -1,10 +1,7 @@ -use std::{fs::File, io::Write}; - -use serde::Deserialize; -use structopt::StructOpt; +use super::*; /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, Deserialize, StructOpt)] +#[derive(Clone, Debug, Deserialize, Parser)] #[structopt(name = "config")] pub enum Config { /// Init `Dioxus.toml` for project/folder. diff --git a/src/cli/create/mod.rs b/src/cli/create/mod.rs index fa553c55a..b92a193b7 100644 --- a/src/cli/create/mod.rs +++ b/src/cli/create/mod.rs @@ -1,15 +1,4 @@ -use std::{ - fs::File, - io::{Read, Write}, - path::PathBuf, - process::{Command, Stdio}, -}; - -use regex::Regex; -use serde::Deserialize; -use structopt::StructOpt; - -use crate::{error::Result, Error}; +use super::*; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Default, Deserialize, StructOpt)] diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4a72e00ac..e3bd18b3b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,5 +1,3 @@ -use structopt::StructOpt; - pub mod build; pub mod cfg; pub mod clean; @@ -8,17 +6,38 @@ pub mod create; pub mod serve; pub mod translate; +use std::{io::Write, path::PathBuf}; +use crate::{cfg::ConfigOptsBuild, gen_page}; +use clap::{Parser}; +use serde::Deserialize; +use std::{ + fs::remove_dir_all, + process::{Command, Stdio}, +}; +use std::{fs::File}; +use std::{ + io::{Read}, +}; +use regex::Regex; +use crate::{error::Result, Error}; +use crate::{cfg::ConfigOptsServe, server, CrateConfig}; +use html_parser::Dom; +use html_parser::Element; +use html_parser::Node; +use std::fmt::{Display, Formatter}; +use std::process::exit; + /// Build, bundle, & ship your Dioxus app. /// /// -#[derive(StructOpt)] -#[structopt(name = "dioxus")] +#[derive(Parser, Debug)] +#[clap(name = "dioxus")] pub struct Cli { - #[structopt(subcommand)] + #[clap(subcommand)] pub action: Commands, /// Enable verbose logging. - #[structopt(short)] + #[clap(short)] pub v: bool, // // // note: dioxus is still roughly compatible with trunk @@ -27,7 +46,7 @@ pub struct Cli { // pub config: Option, } -#[derive(StructOpt)] +#[derive(Parser)] pub enum Commands { /// Build the Rust WASM app and all of its assets. Build(build::Build), diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 8f26e0f30..d7184f5f9 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,9 +1,7 @@ -use crate::{cfg::ConfigOptsServe, gen_page, server, CrateConfig}; -use std::{io::Write, path::PathBuf, process::{Command, Stdio}}; -use structopt::StructOpt; +use super::*; /// Run the WASM project on dev-server -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Parser)] #[structopt(name = "serve")] pub struct Serve { #[structopt(flatten)] diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index a717d632a..4e23533aa 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -1,16 +1,7 @@ -use anyhow::Result; -use html_parser::Dom; -use html_parser::Element; -use html_parser::Node; -#[allow(unused_imports)] -use std::fmt::{Display, Formatter}; -use std::io::Read; -use std::path::PathBuf; -use std::process::exit; -use structopt::StructOpt; +use super::*; /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, StructOpt)] +#[derive(Clone, Debug, Parser)] #[structopt(name = "translate")] pub struct Translate { /// Activate debug mode diff --git a/tests/main.rs b/tests/main.rs new file mode 100644 index 000000000..f9bb6a890 --- /dev/null +++ b/tests/main.rs @@ -0,0 +1,4 @@ +#[test] +fn ready() { + println!("Compiled successfully!") +} \ No newline at end of file From ce628e1197095c778432fcb51df6d5f176a4f868 Mon Sep 17 00:00:00 2001 From: Aster Date: Wed, 9 Feb 2022 16:11:02 +0800 Subject: [PATCH 052/516] Fix compiler errors --- Cargo.toml | 2 ++ src/cli/build/mod.rs | 9 ++++----- src/cli/cfg.rs | 16 ++++++++-------- src/cli/clean/mod.rs | 4 ++-- src/cli/config/mod.rs | 9 +++++---- src/cli/create/mod.rs | 20 ++++++++------------ src/cli/mod.rs | 33 +++++++++++++++++---------------- src/cli/serve/mod.rs | 10 +++++----- src/cli/translate/mod.rs | 10 +++++----- src/error.rs | 37 +++++++++++++++++++++++++++++++++++++ src/main.rs | 4 ++-- src/server/mod.rs | 3 ++- tests/main.rs | 8 ++++---- 13 files changed, 101 insertions(+), 64 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89943ec62..3ae7b4610 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ tokio = { version = "1.16.1", features = ["full"] } atty = "0.2.14" regex = "1.5.4" chrono = "0.4.19" +anyhow = "1.0.53" +hyper = "0.14.17" axum = { version = "0.4.5", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index b4d7f5fe4..1c665abd3 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -1,16 +1,15 @@ use super::*; - /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Parser)] -#[structopt(name = "build")] +#[clap(name = "build")] pub struct Build { - #[structopt(flatten)] + #[clap(flatten)] pub build: ConfigOptsBuild, } impl Build { - pub fn build(self) -> anyhow::Result<()> { + pub fn build(self) -> Result<()> { let mut crate_config = crate::CrateConfig::new()?; // change the release state. @@ -28,7 +27,7 @@ impl Build { crate::builder::build_desktop(&crate_config)?; } _ => { - return Err(anyhow::anyhow!("Unsoppurt platform target.")); + return custom_error!("Unsoppurt platform target."); } } diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index fdd627e85..f1e3d0845 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -4,40 +4,40 @@ use super::*; #[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsBuild { /// The index HTML file to drive the bundling process [default: index.html] - #[structopt(parse(from_os_str))] + #[clap(parse(from_os_str))] pub target: Option, /// Build in release mode [default: false] - #[structopt(long)] + #[clap(long)] #[serde(default)] pub release: bool, /// Build a example [default: ""] - #[structopt(long)] + #[clap(long)] pub example: Option, /// Build platform: support Web & Desktop [default: "web"] - #[structopt(long, default_value = "web")] + #[clap(long, default_value = "web")] pub platform: String, } #[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsServe { /// The index HTML file to drive the bundling process [default: index.html] - #[structopt(parse(from_os_str))] + #[clap(parse(from_os_str))] pub target: Option, /// Build a example [default: ""] - #[structopt(long)] + #[clap(long)] pub example: Option, /// Build in release mode [default: false] - #[structopt(long)] + #[clap(long)] #[serde(default)] pub release: bool, /// Build platform: support Web & Desktop [default: "web"] - #[structopt(long, default_value = "web")] + #[clap(long, default_value = "web")] pub platform: String, } diff --git a/src/cli/clean/mod.rs b/src/cli/clean/mod.rs index 334f93572..74a99fec2 100644 --- a/src/cli/clean/mod.rs +++ b/src/cli/clean/mod.rs @@ -2,11 +2,11 @@ use super::*; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Parser)] -#[structopt(name = "clean")] +#[clap(name = "clean")] pub struct Clean {} impl Clean { - pub fn clean(self) -> anyhow::Result<()> { + pub fn clean(self) -> Result<()> { let crate_config = crate::CrateConfig::new()?; let output = Command::new("cargo") diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index e5d7bdbe4..3cbf368e0 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -1,8 +1,9 @@ use super::*; + /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, Deserialize, Parser)] -#[structopt(name = "config")] +#[derive(Clone, Debug, Deserialize, Subcommand)] +#[clap(name = "config")] pub enum Config { /// Init `Dioxus.toml` for project/folder. Init { @@ -10,14 +11,14 @@ pub enum Config { name: String, /// Cover old config - #[structopt(long)] + #[clap(long)] #[serde(default)] force: bool, }, } impl Config { - pub fn config(self) -> anyhow::Result<()> { + pub fn config(self) -> Result<()> { let crate_root = crate::cargo::crate_root()?; match self { Config::Init { name, force } => { diff --git a/src/cli/create/mod.rs b/src/cli/create/mod.rs index b92a193b7..f00bc5dd9 100644 --- a/src/cli/create/mod.rs +++ b/src/cli/create/mod.rs @@ -1,15 +1,16 @@ use super::*; +use crate::custom_error; /// Build the Rust WASM app and all of its assets. -#[derive(Clone, Debug, Default, Deserialize, StructOpt)] -#[structopt(name = "create")] +#[derive(Clone, Debug, Default, Deserialize, Parser)] +#[clap(name = "create")] pub struct Create { /// Init project name - #[structopt(default_value = ".")] + #[clap(default_value = ".")] name: String, /// Template path - #[structopt(default_value = "gh:dioxuslabs/dioxus-template", long)] + #[clap(default_value = "gh:dioxuslabs/dioxus-template", long)] template: String, } @@ -23,10 +24,7 @@ impl Create { let project_path = PathBuf::from(&self.name); if project_path.join("Dioxus.toml").is_file() || project_path.join("Cargo.toml").is_file() { - return Err(Error::Other(anyhow::anyhow!( - "🧨 Folder '{}' is initialized.", - &self.name - ))); + return custom_error!("🧨 Folder '{}' is initialized.", &self.name); } log::info!("🔧 Start to create a new project '{}'.", self.name); @@ -47,9 +45,7 @@ impl Create { .stderr(Stdio::inherit()) .output()?; if !install_output.status.success() { - return Err(Error::Other(anyhow::anyhow!( - "Try to install cargo-generate failed." - ))); + return custom_error!("Try to install cargo-generate failed."); } } @@ -63,7 +59,7 @@ impl Create { .output()?; if !generate_output.status.success() { - return Err(Error::Other(anyhow::anyhow!("Generate project failed."))); + return custom_error!("Generate project failed."); } let mut dioxus_file = File::open(project_path.join("Dioxus.toml"))?; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index e3bd18b3b..8ec374933 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -6,31 +6,31 @@ pub mod create; pub mod serve; pub mod translate; -use std::{io::Write, path::PathBuf}; +use crate::custom_error; use crate::{cfg::ConfigOptsBuild, gen_page}; -use clap::{Parser}; +use crate::{cfg::ConfigOptsServe, server, CrateConfig}; +use crate::{error::Result, Error}; +use clap::Parser; +use html_parser::Dom; +use html_parser::Element; +use html_parser::Node; +use regex::Regex; use serde::Deserialize; +use std::fmt::{Display, Formatter}; +use std::fs::File; +use std::io::Read; +use std::process::exit; use std::{ fs::remove_dir_all, process::{Command, Stdio}, }; -use std::{fs::File}; -use std::{ - io::{Read}, -}; -use regex::Regex; -use crate::{error::Result, Error}; -use crate::{cfg::ConfigOptsServe, server, CrateConfig}; -use html_parser::Dom; -use html_parser::Element; -use html_parser::Node; -use std::fmt::{Display, Formatter}; -use std::process::exit; +use std::{io::Write, path::PathBuf}; +use clap::{Subcommand}; /// Build, bundle, & ship your Dioxus app. /// /// -#[derive(Parser, Debug)] +#[derive(Parser)] #[clap(name = "dioxus")] pub struct Cli { #[clap(subcommand)] @@ -42,7 +42,7 @@ pub struct Cli { // // // 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")] + // #[clap(long, parse(from_os_str), env = "TRUNK_CONFIG")] // pub config: Option, } @@ -59,5 +59,6 @@ pub enum Commands { /// Clean output artifacts. Clean(clean::Clean), /// Dioxus config file controls. + #[clap(subcommand)] Config(config::Config), } diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index d7184f5f9..d627ace47 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -2,14 +2,14 @@ use super::*; /// Run the WASM project on dev-server #[derive(Clone, Debug, Parser)] -#[structopt(name = "serve")] +#[clap(name = "serve")] pub struct Serve { - #[structopt(flatten)] + #[clap(flatten)] pub serve: ConfigOptsServe, } impl Serve { - pub async fn serve(self) -> anyhow::Result<()> { + pub async fn serve(self) -> Result<()> { let mut crate_config = crate::CrateConfig::new()?; // change the relase state. @@ -49,7 +49,7 @@ impl Serve { return Ok(()); } _ => { - return Err(anyhow::anyhow!("Unsoppurt platform target.")); + return custom_error!("Unsoppurt platform target."); } } @@ -62,7 +62,7 @@ impl Serve { Ok(()) } - pub fn regen_dev_page(crate_config: &CrateConfig) -> anyhow::Result<()> { + pub fn regen_dev_page(crate_config: &CrateConfig) -> Result<()> { let serve_html = gen_page(&crate_config.dioxus_config, true); let mut file = std::fs::File::create( diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 4e23533aa..55ba101cd 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -2,24 +2,24 @@ use super::*; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Parser)] -#[structopt(name = "translate")] +#[clap(name = "translate")] pub struct Translate { /// Activate debug mode // short and long flags (-d, --debug) will be deduced from the field's name - #[structopt(short, long)] + #[clap(short, long)] pub component: bool, /// Input file - #[structopt(short, long)] + #[clap(short, long)] pub file: Option, /// Output file, stdout if not present - #[structopt(parse(from_os_str))] + #[clap(parse(from_os_str))] pub output: Option, } impl Translate { - pub fn translate(self) -> anyhow::Result<()> { + pub fn translate(self) -> Result<()> { let Translate { component, output, diff --git a/src/error.rs b/src/error.rs index 8684abf74..d40868333 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,15 @@ pub enum Error { #[error("I/O Error: {0}")] IO(#[from] std::io::Error), + #[error("Format Error: {0}")] + FormatError(#[from] std::fmt::Error), + + #[error("Format failed: {0}")] + ParseError(String), + + #[error("Runtime Error: {0}")] + RuntimeError(String), + #[error("Failed to write error")] FailedToWrite, @@ -20,6 +29,9 @@ pub enum Error { #[error("Failed to write error")] CargoError(String), + #[error("{0}")] + CustomError(String), + #[error(transparent)] Other(#[from] anyhow::Error), } @@ -35,3 +47,28 @@ impl From for Error { Error::Unique(s) } } + +impl From for Error { + fn from(e: html_parser::Error) -> Self { + Self::ParseError(e.to_string()) + } +} + +impl From for Error { + fn from(e: hyper::Error) -> Self { + Self::RuntimeError(e.to_string()) + } +} + +#[macro_export] +macro_rules! custom_error { + ($msg:literal $(,)?) => { + Err(Error::CustomError($msg.to_string())) + }; + ($err:expr $(,)?) => { + Err(Error::from($err)) + }; + ($fmt:expr, $($arg:tt)*) => { + Err(Error::CustomError(format!($fmt, $($arg)*))) + }; +} diff --git a/src/main.rs b/src/main.rs index 6b8c177db..97d4a3bb4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ +use clap::Parser; use dioxus_cli::*; -use structopt::StructOpt; #[tokio::main] async fn main() -> Result<()> { - let args = Cli::from_args(); + let args = Cli::parse(); set_up_logging(); match args.action { diff --git a/src/server/mod.rs b/src/server/mod.rs index 880f25ceb..f312e07c2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -10,6 +10,7 @@ use notify::{RecommendedWatcher, Watcher}; use std::{path::PathBuf, sync::Arc}; use tower_http::services::ServeDir; +use crate::Result; use crate::{builder, serve::Serve, CrateConfig}; use tokio::sync::broadcast; @@ -17,7 +18,7 @@ struct WsRelodState { update: broadcast::Sender, } -pub async fn startup(config: CrateConfig) -> anyhow::Result<()> { +pub async fn startup(config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); diff --git a/tests/main.rs b/tests/main.rs index f9bb6a890..1849f5dc8 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,4 +1,4 @@ -#[test] -fn ready() { - println!("Compiled successfully!") -} \ No newline at end of file +#[test] +fn ready() { + println!("Compiled successfully!") +} From 59ebb9ec116449bcd77ab554b34a6bb8e09796b9 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Wed, 9 Feb 2022 21:11:41 +0800 Subject: [PATCH 053/516] feat: add `default_platform` config --- src/assets/dioxus.toml | 15 ++++++++------- src/cli/build/mod.rs | 10 +++++++++- src/cli/cfg.rs | 12 ++++++------ src/cli/serve/mod.rs | 16 ++++++++++++++-- src/config.rs | 4 ++-- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 717b070e2..31e306079 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -1,23 +1,24 @@ [application] -# App (Project) Name +# dioxus project name name = "{{project-name}}" -# Dioxus App Support Platform -# desktop, web, mobile, ssr -platforms = ["web"] +# default platfrom +# you can also use `dioxus serve/build --platform XXX` to use other platform +# value: web | desktop +default_platform = "web" # Web `build` & `serve` dist path out_dir = "dist" +# resource (static) file folder +public_dir = "public" + [web.app] # HTML title tag content title = "dioxus | ⛺" -# resource (static) file folder -public_dir = "public" - [web.watcher] watch_path = ["src"] diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 684cec58c..bae291b7e 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -22,7 +22,15 @@ impl Build { crate_config.as_example(self.build.example.unwrap()); } - match self.build.platform.as_str() { + let platform = self.build.platform.unwrap_or_else(|| { + crate_config + .dioxus_config + .application + .default_platform + .clone() + }); + + match platform.as_str() { "web" => { crate::builder::build(&crate_config)?; } diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index ece00e07e..2271f0453 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -19,9 +19,9 @@ pub struct ConfigOptsBuild { #[structopt(long)] pub example: Option, - /// Build platform: support Web & Desktop [default: "web"] - #[structopt(long, default_value = "web")] - pub platform: String, + /// Build platform: support Web & Desktop [default: "default_platform"] + #[structopt(long)] + pub platform: Option, } #[derive(Clone, Debug, Default, Deserialize, StructOpt)] @@ -39,9 +39,9 @@ pub struct ConfigOptsServe { #[serde(default)] pub release: bool, - /// Build platform: support Web & Desktop [default: "web"] - #[structopt(long, default_value = "web")] - pub platform: String, + /// Build platform: support Web & Desktop [default: "default_platform"] + #[structopt(long)] + pub platform: Option, } /// Ensure the given value for `--public-url` is formatted correctly. diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 8f26e0f30..ff263b6b5 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,5 +1,9 @@ use crate::{cfg::ConfigOptsServe, gen_page, server, CrateConfig}; -use std::{io::Write, path::PathBuf, process::{Command, Stdio}}; +use std::{ + io::Write, + path::PathBuf, + process::{Command, Stdio}, +}; use structopt::StructOpt; /// Run the WASM project on dev-server @@ -21,7 +25,15 @@ impl Serve { crate_config.as_example(self.serve.example.unwrap()); } - match self.serve.platform.as_str() { + let platform = self.serve.platform.unwrap_or_else(|| { + crate_config + .dioxus_config + .application + .default_platform + .clone() + }); + + match platform.as_str() { "web" => { crate::builder::build(&crate_config)?; } diff --git a/src/config.rs b/src/config.rs index 1151445ef..a7f5d1fec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,7 +31,7 @@ impl Default for DioxusConfig { Self { application: ApplicationConfig { name: "dioxus".into(), - platforms: vec![String::from("web")], + default_platform: "web".to_string(), out_dir: Some(PathBuf::from("dist")), }, web: WebConfig { @@ -59,7 +59,7 @@ impl Default for DioxusConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApplicationConfig { pub name: String, - pub platforms: Vec, + pub default_platform: String, pub out_dir: Option, } From a0356c7f36cc8862ec3f49f92d07330070e3fa3e Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Wed, 9 Feb 2022 21:25:29 +0800 Subject: [PATCH 054/516] fix: clean `out_dir` --- src/builder.rs | 6 ++++-- src/cli/build/mod.rs | 2 +- src/cli/serve/mod.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 257072a6a..24168a934 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -120,7 +120,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { Ok(()) } -pub fn build_desktop(config: &CrateConfig) -> Result<()> { +pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { log::info!("🚅 Running build [Desktop] command..."); let mut cmd = Command::new("cargo"); @@ -146,7 +146,9 @@ pub fn build_desktop(config: &CrateConfig) -> Result<()> { } if output.status.success() { - if config.out_dir.is_dir() { + // this code will clean the output dir. + // if using the serve, we will not clean the out_dir. + if config.out_dir.is_dir() && !is_serve { remove_dir_all(&config.out_dir)?; } diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index bae291b7e..a29ba654b 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -35,7 +35,7 @@ impl Build { crate::builder::build(&crate_config)?; } "desktop" => { - crate::builder::build_desktop(&crate_config)?; + crate::builder::build_desktop(&crate_config, false)?; } _ => { return Err(anyhow::anyhow!("Unsoppurt platform target.")); diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index ff263b6b5..fe70f7914 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -38,7 +38,7 @@ impl Serve { crate::builder::build(&crate_config)?; } "desktop" => { - crate::builder::build_desktop(&crate_config)?; + crate::builder::build_desktop(&crate_config, true)?; match &crate_config.executable { crate::ExecutableType::Binary(name) From 653ee254c036653674a984cb716d0239c4c38f27 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Wed, 9 Feb 2022 21:30:00 +0800 Subject: [PATCH 055/516] docs: update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7bc1fa131..31e957111 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ dioxus-cli (inspired by wasm-pack and webpack) is a tool to help get dioxus proj ## Installation ``` -$ cargo install dioxus-cli +$ cargo install --git https://github.com/DioxusLabs/cli ``` Now, `dioxus` is in your path, you can use `dioxus --help` to get the help document. From 9d8b95dfc43023da050943300df86ba2612c72a6 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Wed, 9 Feb 2022 21:59:26 +0800 Subject: [PATCH 056/516] feat: add `public_dir` for other platform --- src/builder.rs | 31 ++++++++++++++++++++++++++++++- src/config.rs | 6 +++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 24168a934..1458d52ba 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -91,6 +91,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { .out_name(&dioxus_config.application.name) .generate(&bindgen_outdir)?; + // this code will copy all public file to the output dir let copy_options = fs_extra::dir::CopyOptions { overwrite: true, skip_exist: false, @@ -183,9 +184,37 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { file_name }; - create_dir_all(&config.out_dir)?; + if !config.out_dir.is_dir() { + create_dir_all(&config.out_dir)?; + } copy(res_path, &config.out_dir.join(target_file))?; + // this code will copy all public file to the output dir + if config.public_dir.is_dir() { + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + skip_exist: false, + buffer_size: 64000, + copy_inside: false, + content_only: false, + depth: 0, + }; + + for entry in std::fs::read_dir(&config.public_dir)? { + let path = entry?.path(); + if path.is_file() { + std::fs::copy(&path, &config.out_dir.join(path.file_name().unwrap()))?; + } else { + match fs_extra::dir::copy(&path, &config.out_dir, ©_options) { + Ok(_) => {} + Err(e) => { + log::warn!("Error copying dir: {}", e); + } + } + } + } + } + log::info!( "🚩 Build completed: [./{}]", config diff --git a/src/config.rs b/src/config.rs index a7f5d1fec..5949310dd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -33,11 +33,11 @@ impl Default for DioxusConfig { name: "dioxus".into(), default_platform: "web".to_string(), out_dir: Some(PathBuf::from("dist")), + public_dir: Some(PathBuf::from("public")), }, web: WebConfig { app: WebAppConfing { title: Some("dioxus | ⛺".into()), - public_dir: Some(PathBuf::from("public")), }, watcher: WebWatcherConfing { watch_path: Some(vec![PathBuf::from("src")]), @@ -61,6 +61,7 @@ pub struct ApplicationConfig { pub name: String, pub default_platform: String, pub out_dir: Option, + pub public_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -73,7 +74,6 @@ pub struct WebConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebAppConfing { pub title: Option, - pub public_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -130,7 +130,7 @@ impl CrateConfig { let cargo_def = &crate_dir.join("Cargo.toml"); - let public_dir = match dioxus_config.web.app.public_dir { + let public_dir = match dioxus_config.application.public_dir { Some(ref v) => crate_dir.join(v), None => crate_dir.join("public"), }; From 62712219406ef95d83060bf637c0fa831bcc30d8 Mon Sep 17 00:00:00 2001 From: Aster Date: Thu, 10 Feb 2022 04:02:09 +0800 Subject: [PATCH 057/516] Fix format and clippy --- rustfmt.toml | 8 ++++++++ src/builder.rs | 12 +++++------- src/cli/config/mod.rs | 1 - src/cli/mod.rs | 32 +++++++++++++------------------- src/server/mod.rs | 3 +-- 5 files changed, 27 insertions(+), 29 deletions(-) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..9071fb4b5 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,8 @@ +version = "Two" +edition = "2018" + +imports_granularity = "Crate" +#use_small_heuristics = "Max" +#control_brace_style = "ClosingNextLine" +normalize_comments = true +format_code_in_doc_comments = true \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 257072a6a..f3090f85d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -11,13 +11,11 @@ use std::{ use wasm_bindgen_cli_support::Bindgen; 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?) - [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 - */ + // [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 + // [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 let CrateConfig { out_dir, diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index 3cbf368e0..6d672b7a5 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -1,6 +1,5 @@ use super::*; - /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Deserialize, Subcommand)] #[clap(name = "config")] diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 8ec374933..1b5ba0be7 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -6,30 +6,25 @@ pub mod create; pub mod serve; pub mod translate; -use crate::custom_error; -use crate::{cfg::ConfigOptsBuild, gen_page}; -use crate::{cfg::ConfigOptsServe, server, CrateConfig}; -use crate::{error::Result, Error}; -use clap::Parser; -use html_parser::Dom; -use html_parser::Element; -use html_parser::Node; +use crate::{ + cfg::{ConfigOptsBuild, ConfigOptsServe}, + custom_error, + error::Result, + gen_page, server, CrateConfig, Error, +}; +use clap::{Parser, Subcommand}; +use html_parser::{Dom, Element, Node}; use regex::Regex; use serde::Deserialize; -use std::fmt::{Display, Formatter}; -use std::fs::File; -use std::io::Read; -use std::process::exit; use std::{ - fs::remove_dir_all, - process::{Command, Stdio}, + fmt::{Display, Formatter}, + fs::{remove_dir_all, File}, + io::{Read, Write}, + path::PathBuf, + process::{exit, Command, Stdio}, }; -use std::{io::Write, path::PathBuf}; -use clap::{Subcommand}; /// Build, bundle, & ship your Dioxus app. -/// -/// #[derive(Parser)] #[clap(name = "dioxus")] pub struct Cli { @@ -39,7 +34,6 @@ pub struct Cli { /// Enable verbose logging. #[clap(short)] 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")] diff --git a/src/server/mod.rs b/src/server/mod.rs index f312e07c2..38e37d60b 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -10,8 +10,7 @@ use notify::{RecommendedWatcher, Watcher}; use std::{path::PathBuf, sync::Arc}; use tower_http::services::ServeDir; -use crate::Result; -use crate::{builder, serve::Serve, CrateConfig}; +use crate::{builder, serve::Serve, CrateConfig, Result}; use tokio::sync::broadcast; struct WsRelodState { From 936fefbbf8da475d4b1a915c1d040161b9daee3c Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Fri, 11 Feb 2022 16:54:35 +0800 Subject: [PATCH 058/516] feat: commit code --- src/builder.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 1458d52ba..c1e9fc0a7 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -5,6 +5,7 @@ use crate::{ }; use std::{ fs::{copy, create_dir_all, remove_dir_all}, + panic, path::PathBuf, process::Command, }; @@ -62,9 +63,6 @@ pub fn build(config: &CrateConfig) -> Result<()> { // [2] Establish the output directory structure let bindgen_outdir = out_dir.join("assets").join("dioxus"); - // [3] Bindgen the final binary for use easy linking - let mut bindgen_builder = Bindgen::new(); - let release_type = match config.release { true => "release", false => "debug", @@ -80,16 +78,25 @@ pub fn build(config: &CrateConfig) -> Result<()> { .join(format!("{}.wasm", name)), }; - bindgen_builder - .input_path(input_path) - .web(true)? - .debug(true) - .demangle(true) - .keep_debug(true) - .remove_name_section(false) - .remove_producers_section(false) - .out_name(&dioxus_config.application.name) - .generate(&bindgen_outdir)?; + let bindgen_result = panic::catch_unwind(move || { + // [3] Bindgen the final binary for use easy linking + let mut bindgen_builder = Bindgen::new(); + + bindgen_builder + .input_path(input_path) + .web(true).unwrap() + .debug(true) + .demangle(true) + .keep_debug(true) + .remove_name_section(false) + .remove_producers_section(false) + .out_name(&dioxus_config.application.name) + .generate(&bindgen_outdir).unwrap(); + }); + if bindgen_result.is_err() { + panic!("Bindgen build failed"); + } + // this code will copy all public file to the output dir let copy_options = fs_extra::dir::CopyOptions { From c3ba5c287decc50d82748ee502de3bc863be04e8 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Thu, 17 Feb 2022 19:28:54 +0800 Subject: [PATCH 059/516] feat: change public to `asset_dir` --- src/assets/dioxus.toml | 2 +- src/builder.rs | 10 +++++----- src/config.rs | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 31e306079..d3f160242 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -12,7 +12,7 @@ default_platform = "web" out_dir = "dist" # resource (static) file folder -public_dir = "public" +asset_dir = "public" [web.app] diff --git a/src/builder.rs b/src/builder.rs index efc5a915a..afd551dd3 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -22,7 +22,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { out_dir, crate_dir, target_dir, - public_dir, + asset_dir, executable, dioxus_config, .. @@ -105,8 +105,8 @@ pub fn build(config: &CrateConfig) -> Result<()> { content_only: false, depth: 0, }; - if public_dir.is_dir() { - for entry in std::fs::read_dir(&public_dir)? { + if asset_dir.is_dir() { + for entry in std::fs::read_dir(&asset_dir)? { let path = entry?.path(); if path.is_file() { std::fs::copy(&path, out_dir.join(path.file_name().unwrap()))?; @@ -195,7 +195,7 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { copy(res_path, &config.out_dir.join(target_file))?; // this code will copy all public file to the output dir - if config.public_dir.is_dir() { + if config.asset_dir.is_dir() { let copy_options = fs_extra::dir::CopyOptions { overwrite: true, skip_exist: false, @@ -205,7 +205,7 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { depth: 0, }; - for entry in std::fs::read_dir(&config.public_dir)? { + for entry in std::fs::read_dir(&config.asset_dir)? { let path = entry?.path(); if path.is_file() { std::fs::copy(&path, &config.out_dir.join(path.file_name().unwrap()))?; diff --git a/src/config.rs b/src/config.rs index 5949310dd..ba0427191 100644 --- a/src/config.rs +++ b/src/config.rs @@ -33,7 +33,7 @@ impl Default for DioxusConfig { name: "dioxus".into(), default_platform: "web".to_string(), out_dir: Some(PathBuf::from("dist")), - public_dir: Some(PathBuf::from("public")), + asset_dir: Some(PathBuf::from("public")), }, web: WebConfig { app: WebAppConfing { @@ -61,7 +61,7 @@ pub struct ApplicationConfig { pub name: String, pub default_platform: String, pub out_dir: Option, - pub public_dir: Option, + pub asset_dir: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -101,7 +101,7 @@ pub struct CrateConfig { pub crate_dir: PathBuf, pub workspace_dir: PathBuf, pub target_dir: PathBuf, - pub public_dir: PathBuf, + pub asset_dir: PathBuf, pub manifest: cargo_toml::Manifest, pub executable: ExecutableType, pub dioxus_config: DioxusConfig, @@ -130,7 +130,7 @@ impl CrateConfig { let cargo_def = &crate_dir.join("Cargo.toml"); - let public_dir = match dioxus_config.application.public_dir { + let asset_dir = match dioxus_config.application.asset_dir { Some(ref v) => crate_dir.join(v), None => crate_dir.join("public"), }; @@ -154,7 +154,7 @@ impl CrateConfig { crate_dir, workspace_dir, target_dir, - public_dir, + asset_dir, manifest, executable, release, From e7acc4d88de55792606014c28c9c235bdb860f41 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Thu, 17 Feb 2022 19:32:56 +0800 Subject: [PATCH 060/516] feat: add `log info` --- src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index afd551dd3..708e1ce69 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -92,7 +92,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { .generate(&bindgen_outdir).unwrap(); }); if bindgen_result.is_err() { - panic!("Bindgen build failed"); + log::error!("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.79` Bindgen crate."); } From 8a0abec82865daf78c5783d897c4d8375c0fbb6c Mon Sep 17 00:00:00 2001 From: YuKun Liu Date: Fri, 18 Feb 2022 09:50:26 +0800 Subject: [PATCH 061/516] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 33d1d4932..a939dfcaf 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ dioxus 0.1.2 Build, bundle, & ship your Dioxus app USAGE: - dioxus.exe [FLAGS] + dioxus [FLAGS] FLAGS: -h, --help Prints help information @@ -42,10 +42,10 @@ SUBCOMMANDS: ## Get Started -You can use the `dioxus crate project_name` to init a dioxus project, its will be clone from the [dioxus-template](https://github.com/DioxusLabs/dioxus-template). +You can use the `dioxus create project_name` to init a dioxus project, its will be clone from the [dioxus-template](https://github.com/DioxusLabs/dioxus-template). or you can assign the template path: ``` dioxus create hello --template gh:dioxuslabs/dioxus-template -``` \ No newline at end of file +``` From d462e032925f3a455eb0808274df69207f057fd5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 18 Feb 2022 15:40:16 -0500 Subject: [PATCH 062/516] fix: indenting --- src/cli/translate/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index a717d632a..3e1f1057c 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -23,7 +23,7 @@ pub struct Translate { pub file: Option, /// Output file, stdout if not present - #[structopt(parse(from_os_str))] + #[structopt(short, long)] pub output: Option, } @@ -176,6 +176,7 @@ fn component(cx: Scope) -> Element {{ } writeln!(f, "\n\nmod icons {{")?; + writeln!(f, "\tuse super::*;")?; let mut id = 0; while let Some(svg) = svg_nodes.pop() { @@ -188,7 +189,8 @@ fn component(cx: Scope) -> Element {{ write_tabs(f, 3)?; render_element(f, svg, 3, &mut svg_nodes, false, &mut 0)?; - writeln!(f, "\t\t))\n\t}}\n")?; + + writeln!(f, "\t\t))\n\t}}")?; id += 1; } From 91a8ce31690d54b8df896254e58206da2816b0cd Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Thu, 10 Mar 2022 11:29:27 +0800 Subject: [PATCH 063/516] feat: config init `platform` arg --- src/assets/autoreload.js | 4 +++- src/assets/dioxus.toml | 2 +- src/cli/config/mod.rs | 13 +++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/assets/autoreload.js b/src/assets/autoreload.js index da62e5eae..fffb3e26c 100644 --- a/src/assets/autoreload.js +++ b/src/assets/autoreload.js @@ -1,3 +1,6 @@ +// Dioxus-CLI +// https://github.com/DioxusLabs/cli + (function () { var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; var url = protocol + '//' + window.location.host + '/_dioxus/ws'; @@ -15,7 +18,6 @@ var ws = new WebSocket(url); ws.onmessage = (ev) => { if (ev.data == "reload") { - // alert("reload!!!"); window.location.reload(); } }; diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index d3f160242..af9cc886b 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -6,7 +6,7 @@ name = "{{project-name}}" # default platfrom # you can also use `dioxus serve/build --platform XXX` to use other platform # value: web | desktop -default_platform = "web" +default_platform = "{{default-platform}}" # Web `build` & `serve` dist path out_dir = "dist" diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index 6d672b7a5..2a1f28fda 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -13,6 +13,10 @@ pub enum Config { #[clap(long)] #[serde(default)] force: bool, + + /// Project default platform + #[clap(long, default_value = "web")] + platform: String, }, } @@ -20,7 +24,11 @@ impl Config { pub fn config(self) -> Result<()> { let crate_root = crate::cargo::crate_root()?; match self { - Config::Init { name, force } => { + Config::Init { + name, + force, + platform, + } => { let conf_path = crate_root.join("Dioxus.toml"); if conf_path.is_file() && !force { log::warn!( @@ -30,7 +38,8 @@ impl Config { } let mut file = File::create(conf_path)?; let content = String::from(include_str!("../../assets/dioxus.toml")) - .replace("{{project-name}}", &name); + .replace("{{project-name}}", &name) + .replace("{{default-platform}}", &platform); file.write_all(content.as_bytes())?; log::info!("🚩 Init config file completed."); } From 5fb1b3e20bb5f8282c735bcf7160dfdbcaf695d9 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sat, 12 Mar 2022 13:00:07 +0800 Subject: [PATCH 064/516] feat: commit code --- Cargo.toml | 7 ++++- src/assets/dioxus.toml | 4 ++- src/cli/mod.rs | 1 + src/cli/tools.rs | 69 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/cli/tools.rs diff --git a/Cargo.toml b/Cargo.toml index 3ae7b4610..6a7a2c9d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,12 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + # cli core clap = {version = "3.0.14", features = ["derive"]} thiserror = "1.0.30" wasm-bindgen-cli-support = "0.2.79" + # features log = "0.4.14" fern = { version = "0.6.0", features = ["colored"] } @@ -37,7 +39,10 @@ hyper = "0.14.17" axum = { version = "0.4.5", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } headers = "0.3.7" -# hyper = { version = "0.14.11", features = ["full"] } + +# tools download +dirs = "4.0.0" +reqwest = { version = "0.11", features = ["json"] } [[bin]] path = "src/main.rs" diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index af9cc886b..b897a39a7 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -36,4 +36,6 @@ script = [] # Javascript code file # serve: [dev-server] only -script = [] \ No newline at end of file +script = [] + +[web.tools] diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 1b5ba0be7..9f31c60ae 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -5,6 +5,7 @@ pub mod config; pub mod create; pub mod serve; pub mod translate; +pub mod tools; use crate::{ cfg::{ConfigOptsBuild, ConfigOptsServe}, diff --git a/src/cli/tools.rs b/src/cli/tools.rs new file mode 100644 index 000000000..aa320275e --- /dev/null +++ b/src/cli/tools.rs @@ -0,0 +1,69 @@ +use std::path::PathBuf; + +pub enum Tool { + WasmOpt, +} + +pub fn app_path() { + let data_local = dirs::data_local_dir().unwrap(); + +} + +impl Tool { + pub fn name(&self) -> &str { + match self { + Self::WasmOpt => "wasm-opt", + } + } + + pub fn bin_path(&self) -> &str { + if cfg!(target_os = "windows") { + match self { + Self::WasmOpt => "bin/wasm-opt.exe", + } + } else { + match self { + Self::WasmOpt => "bin/wasm-opt", + } + } + } + + pub fn target_platform(&self) -> &str { + match self { + Self::WasmOpt => { + if cfg!(target_os = "windows") { + "windows" + } else if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "linux") { + "linux" + } else { + panic!("unsupported platformm"); + } + } + } + } + + pub fn download_url(&self) -> String { + match self { + Self::WasmOpt => { + format!( + "https://github.com/WebAssembly/binaryen/releases/download/version_105/binaryen-version_105-x86_64-{target}.tar.gz", + target = self.target_platform() + ) + } + } + } + + pub async fn download_package(&self) { + + let download_dir = dirs::download_dir().unwrap(); + let download_url = self.download_url(); + + let resp = reqwest::get(download_url).await.unwrap(); + + resp. + + } + +} From 25ff6c5937b8907a734d05b6936f5f20e8113b5b Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sat, 12 Mar 2022 23:48:05 +0800 Subject: [PATCH 065/516] feat: commit code --- Cargo.toml | 2 +- src/cli/tools.rs | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a7a2c9d0..0b96d72a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ headers = "0.3.7" # tools download dirs = "4.0.0" -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns"] } [[bin]] path = "src/main.rs" diff --git a/src/cli/tools.rs b/src/cli/tools.rs index aa320275e..a042e7565 100644 --- a/src/cli/tools.rs +++ b/src/cli/tools.rs @@ -1,12 +1,29 @@ -use std::path::PathBuf; +use std::{fs::create_dir_all, path::PathBuf}; + +use anyhow::Context; +use futures::StreamExt; +use tokio::io::AsyncWriteExt; pub enum Tool { WasmOpt, } -pub fn app_path() { +pub fn app_path() -> PathBuf { let data_local = dirs::data_local_dir().unwrap(); - + let dioxus_dir = data_local.join("dioxus"); + if !dioxus_dir.is_dir() { + create_dir_all(&dioxus_dir).unwrap(); + } + dioxus_dir +} + +pub fn temp_path() -> PathBuf { + let app_path = app_path(); + let temp_path = app_path.join("temp"); + if !temp_path.is_dir() { + create_dir_all(&temp_path).unwrap(); + } + temp_path } impl Tool { @@ -55,15 +72,23 @@ impl Tool { } } - pub async fn download_package(&self) { - - let download_dir = dirs::download_dir().unwrap(); + pub async fn download_package(&self) -> anyhow::Result { + let temp_dir = temp_path(); let download_url = self.download_url(); + let temp_out = temp_dir.join(format!("{}-tool.tmp", self.name())); + let mut file = tokio::fs::File::create(&temp_out) + .await + .context("failed creating temporary output file")?; + let resp = reqwest::get(download_url).await.unwrap(); - resp. + let mut res_bytes = resp.bytes_stream(); + while let Some(chunk_res) = res_bytes.next().await { + let chunk = chunk_res.context("error reading chunk from download")?; + let _ = file.write(chunk.as_ref()).await; + } + Ok(temp_out) } - } From eea01f7f8c94c00d6c8ae4cbe6bde29e184552a6 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 13 Mar 2022 13:19:54 +0800 Subject: [PATCH 066/516] feat: commit code --- Cargo.toml | 2 ++ src/cli/mod.rs | 4 ++++ src/cli/tool/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ src/cli/tools.rs | 45 +++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 6 ++++++ 5 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 src/cli/tool/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 0b96d72a2..8beca8f7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ headers = "0.3.7" # tools download dirs = "4.0.0" reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns"] } +flate2 = "1.0.22" +tar = "0.4.38" [[bin]] path = "src/main.rs" diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 9f31c60ae..6e653507d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -6,6 +6,7 @@ pub mod create; pub mod serve; pub mod translate; pub mod tools; +pub mod tool; use crate::{ cfg::{ConfigOptsBuild, ConfigOptsServe}, @@ -56,4 +57,7 @@ pub enum Commands { /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), + /// Install & Manage tools for Dioxus-cli. + #[clap(subcommand)] + Tool(tool::Tool), } diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs new file mode 100644 index 000000000..3f3a86bfc --- /dev/null +++ b/src/cli/tool/mod.rs @@ -0,0 +1,40 @@ +use super::*; + +/// Build the Rust WASM app and all of its assets. +#[derive(Clone, Debug, Deserialize, Subcommand)] +#[clap(name = "tool")] +pub enum Tool { + /// Return all dioxus-cli support tools. + List {}, + Add { + name: String + } +} + +impl Tool { + pub async fn tool(self) -> Result<()> { + match self { + Tool::List {} => { + for item in tools::tool_list() { + if tools::Tool::from_str(item).unwrap().is_installed() { + println!("{item} [installed]"); + } else { + println!("{item}"); + } + } + } + Tool::Add { name } => { + let tool_list = tools::tool_list(); + + if !tool_list.contains(&name.as_str()) { + log::error!("Tool {name} not found."); + return Ok(()); + } + let target_tool = tools::Tool::from_str(&name).unwrap(); + println!("{:?}", target_tool.download_package().await); + } + } + + Ok(()) + } +} diff --git a/src/cli/tools.rs b/src/cli/tools.rs index a042e7565..1eb68e773 100644 --- a/src/cli/tools.rs +++ b/src/cli/tools.rs @@ -8,6 +8,10 @@ pub enum Tool { WasmOpt, } +pub fn tool_list() -> Vec<&'static str> { + vec!["wasm-opt"] +} + pub fn app_path() -> PathBuf { let data_local = dirs::data_local_dir().unwrap(); let dioxus_dir = data_local.join("dioxus"); @@ -26,7 +30,24 @@ pub fn temp_path() -> PathBuf { temp_path } +pub fn tools_path(tool: &Tool) -> PathBuf { + let app_path = app_path(); + let temp_path = app_path.join("tools"); + if !temp_path.is_dir() { + create_dir_all(&temp_path).unwrap(); + } + temp_path.join(tool.name()) +} + +#[allow(clippy::should_implement_trait)] impl Tool { + pub fn from_str(name: &str) -> Option { + match name { + "wasm-opt" => Some(Self::WasmOpt), + _ => None, + } + } + pub fn name(&self) -> &str { match self { Self::WasmOpt => "wasm-opt", @@ -72,11 +93,23 @@ impl Tool { } } - pub async fn download_package(&self) -> anyhow::Result { - let temp_dir = temp_path(); - let download_url = self.download_url(); + pub fn extension(&self) -> &str { + match self { + Self::WasmOpt => "tar.gz", + } + } - let temp_out = temp_dir.join(format!("{}-tool.tmp", self.name())); + pub fn is_installed(&self) -> bool { + tools_path(self).is_dir() + } + + pub fn temp_out_path(&self) -> PathBuf { + temp_path().join(format!("{}-tool.tmp", self.name())) + } + + pub async fn download_package(&self) -> anyhow::Result { + let download_url = self.download_url(); + let temp_out = self.temp_out_path(); let mut file = tokio::fs::File::create(&temp_out) .await .context("failed creating temporary output file")?; @@ -91,4 +124,8 @@ impl Tool { Ok(temp_out) } + + pub async fn install_package() -> anyhow::Result<()> { + Ok(()) + } } diff --git a/src/main.rs b/src/main.rs index 97d4a3bb4..099ff74c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,6 +42,12 @@ async fn main() -> Result<()> { log::error!("config error: {}", e); } } + + Commands::Tool(opts) => { + if let Err(e) = opts.tool().await { + log::error!("tool error: {}", e); + } + } } Ok(()) From 140d7ad7ed1b724b19806ea8999bde84b904646d Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 14 Mar 2022 19:11:12 +0800 Subject: [PATCH 067/516] feat: commit code --- Dioxus.toml | 45 +++++++++++++++++++++++++++++++ src/assets/dioxus.toml | 6 ++++- src/cli/config/mod.rs | 5 ++++ src/cli/tool/mod.rs | 26 +++++++++++++++++- src/cli/tools.rs | 61 ++++++++++++++++++++++++++++-------------- src/config.rs | 4 ++- 6 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 Dioxus.toml diff --git a/Dioxus.toml b/Dioxus.toml new file mode 100644 index 000000000..ab85abe86 --- /dev/null +++ b/Dioxus.toml @@ -0,0 +1,45 @@ +[application] + +# dioxus project name +name = "{{project-name}}" + +# default platfrom +# you can also use `dioxus serve/build --platform XXX` to use other platform +# value: web | desktop +default_platform = "{{default-platform}}" + +# Web `build` & `serve` dist path +out_dir = "dist" + +# resource (static) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[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 = [] + +[application.tools] + +# use binaryen.wasm-opt for output Wasm file +# binaryen just will trigger in `web` platform +binaryen = { wasm_opt = [".PROJECT_OUT"] } \ No newline at end of file diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index b897a39a7..ab85abe86 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -38,4 +38,8 @@ script = [] # serve: [dev-server] only script = [] -[web.tools] +[application.tools] + +# use binaryen.wasm-opt for output Wasm file +# binaryen just will trigger in `web` platform +binaryen = { wasm_opt = [".PROJECT_OUT"] } \ No newline at end of file diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index 2a1f28fda..6422ede27 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -18,6 +18,8 @@ pub enum Config { #[clap(long, default_value = "web")] platform: String, }, + /// Format Print Dioxus-Config. + FormatPrint {} } impl Config { @@ -43,6 +45,9 @@ impl Config { file.write_all(content.as_bytes())?; log::info!("🚩 Init config file completed."); } + Config::FormatPrint {} => { + println!("{:#?}", crate::CrateConfig::new()?.dioxus_config); + } } Ok(()) } diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs index 3f3a86bfc..1b8768497 100644 --- a/src/cli/tool/mod.rs +++ b/src/cli/tool/mod.rs @@ -6,6 +6,9 @@ use super::*; pub enum Tool { /// Return all dioxus-cli support tools. List {}, + /// Get default app install path. + AppPath {}, + /// Install a new tool. Add { name: String } @@ -23,6 +26,9 @@ impl Tool { } } } + Tool::AppPath {} => { + println!("{}", tools::tools_path().to_str().unwrap()); + } Tool::Add { name } => { let tool_list = tools::tool_list(); @@ -31,7 +37,25 @@ impl Tool { return Ok(()); } let target_tool = tools::Tool::from_str(&name).unwrap(); - println!("{:?}", target_tool.download_package().await); + + if target_tool.is_installed() { + log::warn!("Tool {name} is installed."); + return Ok(()); + } + + log::info!("Start to download tool package..."); + if let Err(e) = target_tool.download_package().await { + log::error!("Tool download failed: {e}"); + return Ok(()); + } + + log::info!("Start to install tool package..."); + if let Err(e) = target_tool.install_package().await { + log::error!("Tool install failed: {e}"); + return Ok(()); + } + + log::info!("Tool {name} install successfully!"); } } diff --git a/src/cli/tools.rs b/src/cli/tools.rs index 1eb68e773..257c7f856 100644 --- a/src/cli/tools.rs +++ b/src/cli/tools.rs @@ -1,15 +1,21 @@ -use std::{fs::create_dir_all, path::PathBuf}; +use std::{ + fs::{create_dir_all, File}, + path::PathBuf, +}; use anyhow::Context; +use flate2::read::GzDecoder; use futures::StreamExt; +use tar::Archive; use tokio::io::AsyncWriteExt; +#[derive(Debug, PartialEq, Eq)] pub enum Tool { - WasmOpt, + Binaryen, } pub fn tool_list() -> Vec<&'static str> { - vec!["wasm-opt"] + vec!["binaryen"] } pub fn app_path() -> PathBuf { @@ -30,45 +36,39 @@ pub fn temp_path() -> PathBuf { temp_path } -pub fn tools_path(tool: &Tool) -> PathBuf { +pub fn tools_path() -> PathBuf { let app_path = app_path(); let temp_path = app_path.join("tools"); if !temp_path.is_dir() { create_dir_all(&temp_path).unwrap(); } - temp_path.join(tool.name()) + temp_path } #[allow(clippy::should_implement_trait)] impl Tool { pub fn from_str(name: &str) -> Option { match name { - "wasm-opt" => Some(Self::WasmOpt), + "binaryen" => Some(Self::Binaryen), _ => None, } } pub fn name(&self) -> &str { match self { - Self::WasmOpt => "wasm-opt", + Self::Binaryen => "binaryen", } } pub fn bin_path(&self) -> &str { - if cfg!(target_os = "windows") { - match self { - Self::WasmOpt => "bin/wasm-opt.exe", - } - } else { - match self { - Self::WasmOpt => "bin/wasm-opt", - } + match self { + Self::Binaryen => "bin", } } pub fn target_platform(&self) -> &str { match self { - Self::WasmOpt => { + Self::Binaryen => { if cfg!(target_os = "windows") { "windows" } else if cfg!(target_os = "macos") { @@ -84,7 +84,7 @@ impl Tool { pub fn download_url(&self) -> String { match self { - Self::WasmOpt => { + Self::Binaryen => { format!( "https://github.com/WebAssembly/binaryen/releases/download/version_105/binaryen-version_105-x86_64-{target}.tar.gz", target = self.target_platform() @@ -95,12 +95,12 @@ impl Tool { pub fn extension(&self) -> &str { match self { - Self::WasmOpt => "tar.gz", + Self::Binaryen => "tar.gz", } } pub fn is_installed(&self) -> bool { - tools_path(self).is_dir() + tools_path().join(self.name()).is_dir() } pub fn temp_out_path(&self) -> PathBuf { @@ -125,7 +125,28 @@ impl Tool { Ok(temp_out) } - pub async fn install_package() -> anyhow::Result<()> { + pub async fn install_package(&self) -> anyhow::Result<()> { + let temp_path = self.temp_out_path(); + let tool_path = tools_path(); + + let dir_name = if self == &Tool::Binaryen { + "binaryen-version_105" + } else { + "" + }; + + if self.extension() == "tar.gz" { + let tar_gz = File::open(temp_path)?; + 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()), + )?; + } + Ok(()) } } diff --git a/src/config.rs b/src/config.rs index ba0427191..f7897f5f8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use crate::error::Result; use serde::{Deserialize, Serialize}; -use std::{fs::File, io::Read, path::PathBuf}; +use std::{fs::File, io::Read, path::PathBuf, collections::HashMap}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DioxusConfig { @@ -34,6 +34,7 @@ impl Default for DioxusConfig { default_platform: "web".to_string(), out_dir: Some(PathBuf::from("dist")), asset_dir: Some(PathBuf::from("public")), + tools: None, }, web: WebConfig { app: WebAppConfing { @@ -62,6 +63,7 @@ pub struct ApplicationConfig { pub default_platform: String, pub out_dir: Option, pub asset_dir: Option, + pub tools: Option> } #[derive(Debug, Clone, Serialize, Deserialize)] From 2e83822d415e8d29503defb56eec5cccb0231ed3 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Mon, 14 Mar 2022 22:39:15 +0800 Subject: [PATCH 068/516] feat: commit code --- Dioxus.toml | 4 ++-- src/cli/mod.rs | 1 - src/cli/tool/mod.rs | 2 ++ src/lib.rs | 2 ++ src/{cli => }/tools.rs | 11 +++++++++++ 5 files changed, 17 insertions(+), 3 deletions(-) rename src/{cli => }/tools.rs (92%) diff --git a/Dioxus.toml b/Dioxus.toml index ab85abe86..2db611914 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -1,12 +1,12 @@ [application] # dioxus project name -name = "{{project-name}}" +name = "dioxus-cli" # default platfrom # you can also use `dioxus serve/build --platform XXX` to use other platform # value: web | desktop -default_platform = "{{default-platform}}" +default_platform = "desktop" # Web `build` & `serve` dist path out_dir = "dist" diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 6e653507d..2c91c94dd 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -5,7 +5,6 @@ pub mod config; pub mod create; pub mod serve; pub mod translate; -pub mod tools; pub mod tool; use crate::{ diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs index 1b8768497..51e41e384 100644 --- a/src/cli/tool/mod.rs +++ b/src/cli/tool/mod.rs @@ -1,3 +1,5 @@ +use crate::tools; + use super::*; /// Build the Rust WASM app and all of its assets. diff --git a/src/lib.rs b/src/lib.rs index 395cbc8dc..8cd4b1f2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ pub mod builder; pub mod server; +pub mod tools; + pub use builder::*; pub mod cargo; diff --git a/src/cli/tools.rs b/src/tools.rs similarity index 92% rename from src/cli/tools.rs rename to src/tools.rs index 257c7f856..b5c9d658a 100644 --- a/src/cli/tools.rs +++ b/src/tools.rs @@ -47,6 +47,8 @@ pub fn tools_path() -> PathBuf { #[allow(clippy::should_implement_trait)] impl Tool { + + /// from str to tool enum pub fn from_str(name: &str) -> Option { match name { "binaryen" => Some(Self::Binaryen), @@ -54,18 +56,21 @@ impl Tool { } } + /// get current tool name str pub fn name(&self) -> &str { match self { Self::Binaryen => "binaryen", } } + /// get tool bin dir path pub fn bin_path(&self) -> &str { match self { Self::Binaryen => "bin", } } + /// get target platform pub fn target_platform(&self) -> &str { match self { Self::Binaryen => { @@ -82,6 +87,7 @@ impl Tool { } } + /// get tool package download url pub fn download_url(&self) -> String { match self { Self::Binaryen => { @@ -93,20 +99,24 @@ impl Tool { } } + /// get package extension name pub fn extension(&self) -> &str { match self { Self::Binaryen => "tar.gz", } } + /// check tool state pub fn is_installed(&self) -> bool { tools_path().join(self.name()).is_dir() } + /// get download temp path pub fn temp_out_path(&self) -> PathBuf { temp_path().join(format!("{}-tool.tmp", self.name())) } + /// start to download package pub async fn download_package(&self) -> anyhow::Result { let download_url = self.download_url(); let temp_out = self.temp_out_path(); @@ -125,6 +135,7 @@ impl Tool { Ok(temp_out) } + /// start to install package pub async fn install_package(&self) -> anyhow::Result<()> { let temp_path = self.temp_out_path(); let tool_path = tools_path(); From 3514e341124bd9f0b9f91ad6604c071cd9296685 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 15 Mar 2022 00:22:41 +0800 Subject: [PATCH 069/516] feat: commit code --- Dioxus.toml | 2 +- src/builder.rs | 27 +++++++++++++++++++++++++-- src/cli/build/mod.rs | 1 + src/tools.rs | 34 +++++++++++++++++++++++++++++----- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/Dioxus.toml b/Dioxus.toml index 2db611914..c3e0dd8a0 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -42,4 +42,4 @@ script = [] # use binaryen.wasm-opt for output Wasm file # binaryen just will trigger in `web` platform -binaryen = { wasm_opt = [".PROJECT_OUT"] } \ No newline at end of file +binaryen = { wasm_opt = true } \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 708e1ce69..870e737f0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -82,19 +82,42 @@ pub fn build(config: &CrateConfig) -> Result<()> { bindgen_builder .input_path(input_path) - .web(true).unwrap() + .web(true) + .unwrap() .debug(true) .demangle(true) .keep_debug(true) .remove_name_section(false) .remove_producers_section(false) .out_name(&dioxus_config.application.name) - .generate(&bindgen_outdir).unwrap(); + .generate(&bindgen_outdir) + .unwrap(); }); 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."); } + // check binaryen:wasm-opt tool + let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default(); + if dioxus_tools.contains_key("binaryen") { + let info = dioxus_tools.get("binaryen").unwrap(); + let binaryen = crate::tools::Tool::Binaryen; + if let Some(sub) = info.as_table() { + println!("sub: {sub:?}"); + if sub.contains_key("wasm_opt") + && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false) + { + let target_file = out_dir + .join("assets") + .join("dioxus") + .join(format!("{}_bg.wasm", dioxus_config.application.name)); + println!("tf: {target_file:?}"); + if target_file.is_file() { + binaryen.call("wasm-opt", vec![target_file.to_str().unwrap(), "--print"])?; + } + } + } + } // this code will copy all public file to the output dir let copy_options = fs_extra::dir::CopyOptions { diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 4c043091f..3b4d343af 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -12,6 +12,7 @@ impl Build { pub fn build(self) -> Result<()> { let mut crate_config = crate::CrateConfig::new()?; + // change the release state. crate_config.with_release(self.build.release); diff --git a/src/tools.rs b/src/tools.rs index b5c9d658a..b84cd5579 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,6 +1,7 @@ use std::{ fs::{create_dir_all, File}, path::PathBuf, + process::Command, }; use anyhow::Context; @@ -47,7 +48,6 @@ pub fn tools_path() -> PathBuf { #[allow(clippy::should_implement_trait)] impl Tool { - /// from str to tool enum pub fn from_str(name: &str) -> Option { match name { @@ -152,12 +152,36 @@ impl Tool { 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()), - )?; + std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?; } Ok(()) } + + pub fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result<()> { + let bin_path = tools_path().join(self.name()).join(self.bin_path()); + + let command_file = match self { + Tool::Binaryen => { + if cfg!(target_os = "windows") { + format!("{}.exe", command) + } else { + command.to_string() + } + } + }; + + if !bin_path.join(command_file).is_file() { + return Err(anyhow::anyhow!("Command file not found.")); + } + + let mut command = Command::new(bin_path.to_str().unwrap()); + + command + .args(&args[..]) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + + Ok(()) + } } From 62a2292c16e54fe46c1438d7629ed67831eb5018 Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 15 Mar 2022 17:32:42 +0800 Subject: [PATCH 070/516] feat: commit code --- src/builder.rs | 18 +++++++++++++++--- src/tools.rs | 14 +++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 870e737f0..ac9f72b13 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -95,6 +95,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { }); 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 Ok(()); } // check binaryen:wasm-opt tool @@ -102,8 +103,13 @@ pub fn build(config: &CrateConfig) -> Result<()> { if dioxus_tools.contains_key("binaryen") { let info = dioxus_tools.get("binaryen").unwrap(); let binaryen = crate::tools::Tool::Binaryen; + + if !binaryen.is_installed() { + log::error!("Binaryen tool not found, you can use `dioxus tool add binaryen` to install it."); + return Ok(()); + } + if let Some(sub) = info.as_table() { - println!("sub: {sub:?}"); if sub.contains_key("wasm_opt") && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false) { @@ -111,9 +117,15 @@ pub fn build(config: &CrateConfig) -> Result<()> { .join("assets") .join("dioxus") .join(format!("{}_bg.wasm", dioxus_config.application.name)); - println!("tf: {target_file:?}"); if target_file.is_file() { - binaryen.call("wasm-opt", vec![target_file.to_str().unwrap(), "--print"])?; + binaryen.call( + "wasm-opt", + vec![ + target_file.to_str().unwrap(), + "-o", + target_file.to_str().unwrap(), + ], + )?; } } } diff --git a/src/tools.rs b/src/tools.rs index b84cd5579..937063be0 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -158,7 +158,7 @@ impl Tool { Ok(()) } - pub fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result<()> { + pub fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result> { let bin_path = tools_path().join(self.name()).join(self.bin_path()); let command_file = match self { @@ -171,17 +171,17 @@ impl Tool { } }; - if !bin_path.join(command_file).is_file() { + if !bin_path.join(&command_file).is_file() { return Err(anyhow::anyhow!("Command file not found.")); } - let mut command = Command::new(bin_path.to_str().unwrap()); + let mut command = Command::new(bin_path.join(&command_file).to_str().unwrap()); - command + let output = command .args(&args[..]) .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()); - - Ok(()) + .stderr(std::process::Stdio::inherit()) + .output()?; + Ok(output.stdout) } } From 8b380f2617f3845db6d7006790312290c85cf19c Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Tue, 15 Mar 2022 18:28:19 +0800 Subject: [PATCH 071/516] feat: commit code --- src/builder.rs | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index ac9f72b13..d58af6f43 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -105,29 +105,30 @@ pub fn build(config: &CrateConfig) -> Result<()> { let binaryen = crate::tools::Tool::Binaryen; if !binaryen.is_installed() { - log::error!("Binaryen tool not found, you can use `dioxus tool add binaryen` to install it."); - return Ok(()); - } - - if let Some(sub) = info.as_table() { - if sub.contains_key("wasm_opt") - && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false) - { - let target_file = out_dir - .join("assets") - .join("dioxus") - .join(format!("{}_bg.wasm", dioxus_config.application.name)); - if target_file.is_file() { - binaryen.call( - "wasm-opt", - vec![ - target_file.to_str().unwrap(), - "-o", - target_file.to_str().unwrap(), - ], - )?; + if let Some(sub) = info.as_table() { + if sub.contains_key("wasm_opt") + && sub.get("wasm_opt").unwrap().as_bool().unwrap_or(false) + { + let target_file = out_dir + .join("assets") + .join("dioxus") + .join(format!("{}_bg.wasm", dioxus_config.application.name)); + if target_file.is_file() { + binaryen.call( + "wasm-opt", + vec![ + target_file.to_str().unwrap(), + "-o", + target_file.to_str().unwrap(), + ], + )?; + } } } + } else { + log::warn!( + "Binaryen tool not found, you can use `dioxus tool add binaryen` to install it." + ); } } From 6b0d57abe5c70ebd8cc7cdcc9f14fea5c8b738dd Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 20 Mar 2022 18:39:28 +0800 Subject: [PATCH 072/516] feat: commit code --- Cargo.toml | 2 +- src/server/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8beca8f7d..246e90517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-cli" -version = "0.1.3" +version = "0.1.4" authors = ["Jonathan Kelley"] edition = "2018" description = "CLI tool for developing, testing, and publishing Dioxus apps" diff --git a/src/server/mod.rs b/src/server/mod.rs index 38e37d60b..7d63f3eb6 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -33,6 +33,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { // file watcher: check file change let watcher_conf = config.clone(); let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { + log::info!("Start to rebuild project..."); if chrono::Local::now().timestamp() > last_update_time && builder::build(&watcher_conf).is_ok() { @@ -47,8 +48,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { { let _ = Serve::regen_dev_page(&watcher_conf); } - println!("watcher send reload"); - reload_tx.send("reload".into()).unwrap(); + let _ = reload_tx.send("reload".into()); last_update_time = chrono::Local::now().timestamp(); } }) From e5dfe84eb1f8c821fa875744ed7ec541de8013ad Mon Sep 17 00:00:00 2001 From: mrxiaozhuox Date: Sun, 20 Mar 2022 19:31:30 +0800 Subject: [PATCH 073/516] feat: add `custom html` support --- src/assets/index.html | 2 -- src/builder.rs | 17 +++++++++++++++-- src/cli/config/mod.rs | 13 +++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/assets/index.html b/src/assets/index.html index 9934e490d..925df0227 100644 --- a/src/assets/index.html +++ b/src/assets/index.html @@ -4,11 +4,9 @@ {style_include} -
- {script_include} From 4867cb5cf30f06635142837cf790480b37aa5fdc Mon Sep 17 00:00:00 2001 From: YuKun Liu Date: Sat, 2 Apr 2022 23:51:52 +0800 Subject: [PATCH 089/516] Update config.rs --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 3fc63637c..e664edbfc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -65,7 +65,7 @@ pub struct ApplicationConfig { pub default_platform: String, pub out_dir: Option, pub asset_dir: Option, - pub tools: Option> + pub tools: Option>, pub sub_package: Option, } From 280480de6bd7c9d95910be0932edb31b984827f3 Mon Sep 17 00:00:00 2001 From: Ben Birch Date: Thu, 7 Apr 2022 15:22:46 +1000 Subject: [PATCH 090/516] - set index on 404 to true (highly desired for SPA) - add support for web.base_path in dioxus.toml to allow multi level routes --- src/assets/index.html | 45 +++++++++++++++++++++++-------------------- src/builder.rs | 12 +++++++++--- src/config.rs | 4 +++- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/assets/index.html b/src/assets/index.html index bff411bee..f907aa281 100644 --- a/src/assets/index.html +++ b/src/assets/index.html @@ -1,23 +1,26 @@ - - {app_title} - - - - {style_include} - - -
- - - {script_include} - - + + {app_title} + + + + {style_include} + + + + +
+ + + {script_include} + + + \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 708e1ce69..bacfcd664 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -82,20 +82,21 @@ pub fn build(config: &CrateConfig) -> Result<()> { bindgen_builder .input_path(input_path) - .web(true).unwrap() + .web(true) + .unwrap() .debug(true) .demangle(true) .keep_debug(true) .remove_name_section(false) .remove_producers_section(false) .out_name(&dioxus_config.application.name) - .generate(&bindgen_outdir).unwrap(); + .generate(&bindgen_outdir) + .unwrap(); }); 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."); } - // this code will copy all public file to the output dir let copy_options = fs_extra::dir::CopyOptions { overwrite: true, @@ -278,6 +279,11 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { html = html.replace("{app_name}", &config.application.name); + html = match &config.web.app.base_path { + Some(path) => html.replace("{base_path}", path), + None => html.replace("{base_path}", "."), + }; + let title = config .web .app diff --git a/src/config.rs b/src/config.rs index fbf6f4b9d..9e8b47f7a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -39,11 +39,12 @@ impl Default for DioxusConfig { web: WebConfig { app: WebAppConfing { title: Some("dioxus | ⛺".into()), + base_path: None, }, watcher: WebWatcherConfing { watch_path: Some(vec![PathBuf::from("src")]), reload_html: Some(false), - index_on_404: Some(false), + index_on_404: Some(true), }, resource: WebResourceConfing { dev: WebDevResourceConfing { @@ -77,6 +78,7 @@ pub struct WebConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebAppConfing { pub title: Option, + pub base_path: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] From 714c6dd46ff383abfa749e6c3d9d82bbbfd6bdb5 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 17 Apr 2022 11:24:17 +0800 Subject: [PATCH 091/516] Upgrade `axum` version --- Cargo.toml | 2 +- src/builder.rs | 8 ++++---- src/cli/serve/mod.rs | 15 ++++----------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f5ded9be..21e2dad7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ chrono = "0.4.19" anyhow = "1.0.53" hyper = "0.14.17" -axum = { version = "0.4.5", features = ["ws", "headers"] } +axum = { version = "0.5.1", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } headers = "0.3.7" # tools download diff --git a/src/builder.rs b/src/builder.rs index aaaa0278a..09a410458 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -206,16 +206,16 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { file_name = name.clone(); config .target_dir - .join(release_type.to_string()) - .join(name.to_string()) + .join(release_type) + .join(name) } crate::ExecutableType::Example(name) => { file_name = name.clone(); config .target_dir - .join(release_type.to_string()) + .join(release_type) .join("examples") - .join(name.to_string()) + .join(name) } }; diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 21d29cd93..85bd92c74 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -1,9 +1,9 @@ +use super::*; use std::{ io::Write, path::PathBuf, process::{Command, Stdio}, }; -use super::*; /// Run the WASM project on dev-server #[derive(Clone, Debug, Parser)] @@ -47,16 +47,9 @@ impl Serve { if cfg!(windows) { file.set_extension("exe"); } - Command::new( - crate_config - .out_dir - .join(file) - .to_str() - .unwrap() - .to_string(), - ) - .stdout(Stdio::inherit()) - .output()?; + Command::new(crate_config.out_dir.join(file).to_str().unwrap()) + .stdout(Stdio::inherit()) + .output()?; } } return Ok(()); From 001410f1196f2ea6fe35084b5d489f48d104ca83 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 17 Apr 2022 11:25:53 +0800 Subject: [PATCH 092/516] Format code --- src/builder.rs | 7 ++----- src/cli/build/mod.rs | 1 - src/cli/config/mod.rs | 2 +- src/cli/mod.rs | 2 +- src/cli/tool/mod.rs | 6 ++---- src/config.rs | 2 +- 6 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 09a410458..f7f68e2bc 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -132,7 +132,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { ); } } - + // this code will copy all public file to the output dir let copy_options = fs_extra::dir::CopyOptions { overwrite: true, @@ -204,10 +204,7 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { let mut res_path = match &config.executable { crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => { file_name = name.clone(); - config - .target_dir - .join(release_type) - .join(name) + config.target_dir.join(release_type).join(name) } crate::ExecutableType::Example(name) => { file_name = name.clone(); diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 3b4d343af..4c043091f 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -12,7 +12,6 @@ impl Build { pub fn build(self) -> Result<()> { let mut crate_config = crate::CrateConfig::new()?; - // change the release state. crate_config.with_release(self.build.release); diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs index fb1e046fb..5f9fa20d0 100644 --- a/src/cli/config/mod.rs +++ b/src/cli/config/mod.rs @@ -21,7 +21,7 @@ pub enum Config { /// Format print Dioxus config. FormatPrint {}, /// Create a custom html file. - CustomHtml {} + CustomHtml {}, } impl Config { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 2c91c94dd..d639d28a7 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -4,8 +4,8 @@ pub mod clean; pub mod config; pub mod create; pub mod serve; -pub mod translate; pub mod tool; +pub mod translate; use crate::{ cfg::{ConfigOptsBuild, ConfigOptsServe}, diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs index 51e41e384..d5c4581aa 100644 --- a/src/cli/tool/mod.rs +++ b/src/cli/tool/mod.rs @@ -11,9 +11,7 @@ pub enum Tool { /// Get default app install path. AppPath {}, /// Install a new tool. - Add { - name: String - } + Add { name: String }, } impl Tool { @@ -33,7 +31,7 @@ impl Tool { } Tool::Add { name } => { let tool_list = tools::tool_list(); - + if !tool_list.contains(&name.as_str()) { log::error!("Tool {name} not found."); return Ok(()); diff --git a/src/config.rs b/src/config.rs index 0cdd0bd8b..783c72ccd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use crate::error::Result; use serde::{Deserialize, Serialize}; -use std::{fs::File, io::Read, path::PathBuf, collections::HashMap}; +use std::{collections::HashMap, fs::File, io::Read, path::PathBuf}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DioxusConfig { From 569b294ab9fe98960f89c9eb4e75158f7db4b8cb Mon Sep 17 00:00:00 2001 From: Michael Mroz Date: Sun, 17 Apr 2022 01:13:48 -0700 Subject: [PATCH 093/516] Opt into standards mode --- src/assets/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/assets/index.html b/src/assets/index.html index f57acbd34..87d447816 100644 --- a/src/assets/index.html +++ b/src/assets/index.html @@ -1,3 +1,4 @@ + {app_title} From f203b5f99d7bed69f638073ac86fd911b14f2c19 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 24 Apr 2022 10:44:06 +0800 Subject: [PATCH 094/516] Move `docs` to cli repo --- .github/workflows/docs.yml | 32 ++++++++ docs/.gitignore | 1 + docs/book.toml | 6 ++ docs/src/SUMMARY.md | 11 +++ docs/src/cmd/README.md | 26 +++++++ docs/src/cmd/build.md | 47 ++++++++++++ docs/src/cmd/clean.md | 18 +++++ docs/src/cmd/serve.md | 49 +++++++++++++ docs/src/cmd/translate.md | 57 ++++++++++++++ docs/src/configure.md | 147 +++++++++++++++++++++++++++++++++++++ docs/src/creating.md | 38 ++++++++++ docs/src/installation.md | 24 ++++++ docs/src/introduction.md | 21 ++++++ 13 files changed, 477 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/.gitignore create mode 100644 docs/book.toml create mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/cmd/README.md create mode 100644 docs/src/cmd/build.md create mode 100644 docs/src/cmd/clean.md create mode 100644 docs/src/cmd/serve.md create mode 100644 docs/src/cmd/translate.md create mode 100644 docs/src/configure.md create mode 100644 docs/src/creating.md create mode 100644 docs/src/installation.md create mode 100644 docs/src/introduction.md diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..2638b8a18 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,32 @@ +name: github pages + +on: + push: + paths: + - docs/** + - .github/workflows/docs.yml + branches: + - master + +jobs: + build-deploy: + runs-on: ubuntu-latest + environment: docs + steps: + - uses: actions/checkout@v2 + + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v1 + with: + mdbook-version: "0.4.10" + + - name: Build + run: cd docs && md build -d ../build + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4.2.3 + with: + branch: gh-pages # The branch the action should deploy to. + folder: build # The folder the action should deploy. + clean: false + token: ${{ secrets.DEPLOY_KEY }} # let's pretend I don't need it for now diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..7585238ef --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 000000000..c5c216fe4 --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["YuKun Liu"] +language = "en" +multilingual = false +src = "src" +title = "Dioxus Cli" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 000000000..ed7bcbfaf --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,11 @@ +# Summary + +- [Introduction](./introduction.md) +- [Installation](./installation.md) +- [Create a Project](./creating.md) +- [Configure Project](./configure.md) +- [Commands](./cmd/README.md) + - [Build](./cmd/build.md) + - [Serve](./cmd/serve.md) + - [Clean](./cmd/clean.md) + - [Translate](./cmd/translate.md) \ No newline at end of file diff --git a/docs/src/cmd/README.md b/docs/src/cmd/README.md new file mode 100644 index 000000000..c642b475f --- /dev/null +++ b/docs/src/cmd/README.md @@ -0,0 +1,26 @@ +# Commands + +In this chapter we will introduce all `dioxus-cli` commands. + +> you can also use `dioxus --help` to get cli help info. + +``` +dioxus +Build, bundle, & ship your Dioxus app + +USAGE: + dioxus [OPTIONS] + +OPTIONS: + -h, --help Print help information + -v Enable verbose logging + +SUBCOMMANDS: + build Build the Rust WASM app and all of its assets + clean Clean output artifacts + config Dioxus config file controls + create Init a new project for Dioxus + help Print this message or the help of the given subcommand(s) + serve Build, watch & serve the Rust WASM app and all of its assets + translate Translate some source file into Dioxus code +``` \ No newline at end of file diff --git a/docs/src/cmd/build.md b/docs/src/cmd/build.md new file mode 100644 index 000000000..e81973f2d --- /dev/null +++ b/docs/src/cmd/build.md @@ -0,0 +1,47 @@ +# Build + +The `dioxus build` command can help you `pack & build` a dioxus project. + +``` +dioxus-build +Build the Rust WASM app and all of its assets + +USAGE: + dioxus build [OPTIONS] + +OPTIONS: + --example [default: ""] + --platform [default: "default_platform"] + --release [default: false] +``` + +You can use this command to build project to `out_dir` : + +``` +dioxus build --release +``` + +## Target platform + +Use option `platform` choose build target platform: + +``` +# for desktop project +dioxus build --platform desktop +``` + +`platform` only supports `desktop` & `web`. + +``` +# for web project +dioxus build --platform web +``` + +## Build Example + +You can use `--example {name}` to build a example code. + +``` +# build `example/test` code +dioxus build --exmaple test +``` \ No newline at end of file diff --git a/docs/src/cmd/clean.md b/docs/src/cmd/clean.md new file mode 100644 index 000000000..2f889c1a8 --- /dev/null +++ b/docs/src/cmd/clean.md @@ -0,0 +1,18 @@ +# Clean + +`dioxus clean` will call `target clean` and remove `out_dir` directory. + +``` +dioxus-clean +Clean output artifacts + +USAGE: + dioxus clean +``` + +you can use this command to clean all build cache and the `out_dir` content. + +``` +dioxus clean +``` + diff --git a/docs/src/cmd/serve.md b/docs/src/cmd/serve.md new file mode 100644 index 000000000..e5c10a902 --- /dev/null +++ b/docs/src/cmd/serve.md @@ -0,0 +1,49 @@ +# Serve + +The `dioxus serve` can start a dev server (include hot-reload tool) to run the project. + +``` +dioxus-serve +Build, watch & serve the Rust WASM app and all of its assets + +USAGE: + dioxus serve [OPTIONS] + +OPTIONS: + --example [default: ""] + --platform [default: "default_platform"] + --release [default: false] +``` + +You can use this command to build project and start a `dev server` : + +``` +dioxus serve +``` + +## Target platform + +Use option `platform` choose build target platform: + +``` +# for desktop project +dioxus serve --platform desktop +``` + +`platform` only supports `desktop` & `web`. + +`dev-server` only for `web` project. + +``` +# for web project +dioxus serve --platform web +``` + +## Serve Example + +You can use `--example {name}` to start a example code. + +``` +# build `example/test` code +dioxus serve --exmaple test +``` \ No newline at end of file diff --git a/docs/src/cmd/translate.md b/docs/src/cmd/translate.md new file mode 100644 index 000000000..415a88fba --- /dev/null +++ b/docs/src/cmd/translate.md @@ -0,0 +1,57 @@ +# Translate + +`dioxus translate` can translate some source file into Dioxus code. + +``` +dioxus-translate +Translate some source file into Dioxus code + +USAGE: + dioxus translate [OPTIONS] [OUTPUT] + +ARGS: + Output file, stdout if not present + +OPTIONS: + -c, --component Activate debug mode + -f, --file Input file +``` + +## Translate HTML to stdout + +``` +dioxus transtale --file ./index.html +``` + +## Output in a file + +``` +dioxus translate --component --file ./index.html component.rsx +``` + +set `component` flag will wrap `dioxus rsx` code in a component function. + +## Example + +```html +
+

Hello World

+ Link +
+``` + +Translate HTML to Dioxus component code. + +```rust +fn component(cx: Scope) -> Element { + cx.render(rsx! { + div { + h1 { "Hello World" }, + a { + href: "https://dioxuslabs.com/", + "Link" + } + } + }) +} +``` \ No newline at end of file diff --git a/docs/src/configure.md b/docs/src/configure.md new file mode 100644 index 000000000..f274ce70d --- /dev/null +++ b/docs/src/configure.md @@ -0,0 +1,147 @@ +# Configure Project + +This chapter will introduce `Dioxus.toml` and anatomy the config file. + +## Structure + +We use `toml` to define some info for `dioxus` project. + +### Application + +1. ***name*** - project name & title +2. ***default_platform*** - which platform target for this project. + ``` + # current support: web, desktop + # default: web + default_platform = "web" + ``` + change this to `desktop`, the `dioxus build & serve` will default build desktop app. +3. ***out_dir*** - which directory to put the output file; use `dioxus build & service`, the output will put at this directory, and the `assets` will be also copy to here. + ``` + out_dir = "dist" + ``` +4. ***asset_dir*** - which direcotry to put your `static, assets` file, cli will automatic copy all file to `out_dir`, so you can put some resource file in there, like `CSS, JS, Image` file. + ``` + asset_dir = "public" + ``` + +### Web.App + +Web platform application config: + +1. ***title*** - this value will display on the web page title. like `` tag. + ``` + # HTML title tag content + title = "dioxus app | ⛺" + ``` + +### Web.Watcher + +Web platform `dev-server` watcher config: + +1. ***reload_html*** - a boolean value; when watcher trigger, regenerate `index.html` file. + ``` + # when watcher trigger, regenerate the `index.html` + reload_html = true + ``` +2. ***watch_path*** - which files & directories will be watcher monitoring. + ``` + watch_path = ["src", "public"] + ``` + +### Web.Resource + +Include some `CSS Javascript` resources into html file. + +1. ***style*** - include some style(CSS) file into html. + ``` + style = [ + # include from public_dir. + "./assets/style.css", + # or some asset from online cdn. + "https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.css" + ] + ``` +2. ***script*** - include some script(JS) file into html. + ``` + style = [ + # include from public_dir. + "./assets/index.js", + # or some asset from online cdn. + "https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.js" + ] + ``` + +### Web.Resource.Dev + +Only include resources at `Dev` mode. + +1. ***style*** - include some style(CSS) file into html. + ``` + style = [ + # include from public_dir. + "./assets/style.css", + # or some asset from online cdn. + "https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.css" + ] + ``` +2. ***script*** - include some script(JS) file into html. + ``` + style = [ + # include from public_dir. + "./assets/index.js", + # or some asset from online cdn. + "https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.js" + ] + ``` + +## Config example + +```toml +[application] + +# App (Project) Name +name = "{{project-name}}" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] + +# include `assets` in web platform +[web.resource] + +# CSS style file +style = [] + +# Javascript code file +script = [] + +[web.resource.dev] + +# serve: [dev-server] only + +# CSS style file +style = [] + +# Javascript code file +script = [] +``` \ No newline at end of file diff --git a/docs/src/creating.md b/docs/src/creating.md new file mode 100644 index 000000000..fa725a4f7 --- /dev/null +++ b/docs/src/creating.md @@ -0,0 +1,38 @@ +# Create a Project + +Once you have the Dioxus CLI tool installed, you can use it to create dioxus project. + +## Initializing a default project + +The `dioxus create` command will create a new directory containing a project template. +``` +dioxus create hello-dioxus +``` + +It will clone a default template from github template: [DioxusLabs/dioxus-template](https://github.com/DioxusLabs/dioxus-template) + +> This default template is use for `web` platform application. + +then you can change the current directory in to the project: + +``` +cd hello-dioxus +``` + +> Make sure `wasm32 target` is installed before running the Web project. + +now we can create a `dev server` to display the project: + +``` +dioxus serve +``` + +by default, the dioxus dev server will running at: [`http://127.0.0.1:8080/`](http://127.0.0.1:8080/) + +## Initalizing from other repository + +you can assign which repository you want to create: + +``` +dioxus init hello-dioxus --template=gh:dioxuslabs/dioxus-template +``` \ No newline at end of file diff --git a/docs/src/installation.md b/docs/src/installation.md new file mode 100644 index 000000000..7151de1ff --- /dev/null +++ b/docs/src/installation.md @@ -0,0 +1,24 @@ +# Installation + +There are multiple ways to install the Dioxus CLI tool. Choose any one of the methods below that best suit your needs. + +## Install from latest master version + +We suggest you use `github master` version to install it now. + +We have not yet released the latest version to `crates.io` + +``` +cargo install --git https://github.com/Dioxuslabs/cli +``` + +This will automatically download `Dioxus-CLI` source from github master branch, +and install it in Cargo's global binary directory (`~/.cargo/bin/` by default). + +## Install from `crates.io` version + +``` +cargo install dioxus-cli +``` + +Make sure to add the Cargo bin directory to your `PATH`. \ No newline at end of file diff --git a/docs/src/introduction.md b/docs/src/introduction.md new file mode 100644 index 000000000..2f857a2a5 --- /dev/null +++ b/docs/src/introduction.md @@ -0,0 +1,21 @@ +# Introduction + +📦✨ **Dioxus-Cli** is a tool to help get dioxus projects off the ground. + +![dioxus-logo](https://dioxuslabs.com/guide/images/dioxuslogo_full.png) + +It includes `dev server`, `hot reload` and some `quick command` to help you use dioxus. + +## Features + +- [x] `html` to `rsx` conversion tool +- [x] hot reload for `web` platform +- [x] create dioxus project from `git` repo +- [x] build & pack dioxus project +- [ ] autoformat dioxus `rsx` code + +## Contributors + +Contributors to this guide: + +- [mrxiaozhuox](https://github.com/mrxiaozhuox) \ No newline at end of file From 79df0d0f742f98c7d54a5463e62a19a3ef0b36c8 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 24 Apr 2022 10:47:04 +0800 Subject: [PATCH 095/516] Fixed workflows --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2638b8a18..c6ae8be13 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: mdbook-version: "0.4.10" - name: Build - run: cd docs && md build -d ../build + run: cd docs && mdbook build -d ../build - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@v4.2.3 From 1f1de4d82219125df7ff4654b76c7ef3d7d3ba1b Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 24 Apr 2022 11:03:46 +0800 Subject: [PATCH 096/516] Fixed docs.yml --- .github/workflows/docs.yml | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c6ae8be13..5643e6194 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -2,31 +2,29 @@ name: github pages on: push: - paths: - - docs/** - - .github/workflows/docs.yml branches: - - master + - main + pull_request: jobs: - build-deploy: - runs-on: ubuntu-latest - environment: docs + deploy: + runs-on: ubuntu-20.04 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} steps: - uses: actions/checkout@v2 - name: Setup mdBook uses: peaceiris/actions-mdbook@v1 with: - mdbook-version: "0.4.10" + mdbook-version: '0.4.10' + # mdbook-version: 'latest' - - name: Build - run: cd docs && mdbook build -d ../build + - run: cd docs && mdbook build - - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4.2.3 + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} with: - branch: gh-pages # The branch the action should deploy to. - folder: build # The folder the action should deploy. - clean: false - token: ${{ secrets.DEPLOY_KEY }} # let's pretend I don't need it for now + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/book \ No newline at end of file From 5f7daedc5d0a9515fce711804e1eaf94204cf6df Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 24 Apr 2022 11:22:02 +0800 Subject: [PATCH 097/516] Update docs.yml --- .github/workflows/docs.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5643e6194..2ed974a47 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,9 +22,12 @@ jobs: - run: cd docs && mdbook build - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - if: ${{ github.ref == 'refs/heads/main' }} + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4.2.3 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs/book \ No newline at end of file + branch: gh-pages # The branch the action should deploy to. + folder: docs/book # The folder the action should deploy. + target-folder: docs/nightly/cli + repository-name: dioxuslabs/docsite + clean: false + token: ${{ secrets.DEPLOY_KEY }} # let's pretend I don't need it for now \ No newline at end of file From 04535749e9668b04a37d192fedc258ba6d9168ef Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 23 Apr 2022 23:28:53 -0400 Subject: [PATCH 098/516] chore: clean up some latent items --- TODO.md | 11 ----------- out.rsx | 23 ----------------------- test.html | 5 ----- 3 files changed, 39 deletions(-) delete mode 100644 TODO.md delete mode 100644 out.rsx delete mode 100644 test.html diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 6fd51d591..000000000 --- a/TODO.md +++ /dev/null @@ -1,11 +0,0 @@ - -asd -- [ ] use a TUI for the dev server aspect of things -- [ ] allow any index.html -- [ ] use wasmopt for production builds -- [ ] pass arguments through to `cargo` -- [ ] figure out a bundling strategy -- [ ] an external configuration - - -should we just use trunk? diff --git a/out.rsx b/out.rsx deleted file mode 100644 index b30b76f9e..000000000 --- a/out.rsx +++ /dev/null @@ -1,23 +0,0 @@ - -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/test.html b/test.html deleted file mode 100644 index 33ca3dd1f..000000000 --- a/test.html +++ /dev/null @@ -1,5 +0,0 @@ -
-

- hello -

-
From 3269b2b5aaaa95c55599bc311cc46ade38ca4630 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 23 Apr 2022 23:55:18 -0400 Subject: [PATCH 099/516] feat: add more options to translation --- extension/package.json | 4 +-- extension/src/extension.ts | 5 ++- src/cli/translate/mod.rs | 22 +++++++++--- tests/test.html | 72 +++++++++++++++++++++++--------------- 4 files changed, 68 insertions(+), 35 deletions(-) diff --git a/extension/package.json b/extension/package.json index 0a3d50b67..c492f97e2 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,7 +1,7 @@ { "name": "dioxusstudio", - "displayName": "dioxusStudio", - "description": "Toolkit for IDE support in Dioxus apps", + "displayName": "Dioxus", + "description": "Useful tools for working with Dioxus", "version": "0.0.1", "publisher": "jkelleyrtp", "private": true, diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 5a41c7f5f..4322c48e1 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import { spawn } from "child_process"; export function activate(context: vscode.ExtensionContext) { + function registerCommand(cmd: string) { function convert(cmd: string) { const editor = vscode.window.activeTextEditor;// Get the active text editor @@ -10,7 +11,7 @@ export function activate(context: vscode.ExtensionContext) { if (html.length > 0) { let params = ["translate"]; if (cmd.includes("Component")) params.push("--component"); - params.push("--source"); + params.push("--raw"); params.push(html); const child_proc = spawn("dioxus", params); let result = ''; @@ -23,6 +24,8 @@ export function activate(context: vscode.ExtensionContext) { } } } + + const handle = vscode.commands.registerCommand(cmd, () => convert(cmd)); context.subscriptions.push(handle); } diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index e239ce318..3440ee6b1 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -13,6 +13,10 @@ pub struct Translate { #[clap(short, long)] pub file: Option, + /// Input file + #[clap(short, long)] + pub raw: Option, + /// Output file, stdout if not present #[clap(parse(from_os_str))] pub output: Option, @@ -24,14 +28,20 @@ impl Translate { component, output, file, + raw, } = self; - let contents = match file { - Some(input) => std::fs::read_to_string(&input).unwrap_or_else(|e| { + let contents = match (file, raw) { + (Some(input), None) => std::fs::read_to_string(&input).unwrap_or_else(|e| { log::error!("Cloud not read input file: {}.", e); exit(0); }), - None => { + (None, Some(raw)) => raw, + (Some(_), Some(_)) => { + log::error!("Only one of --file or --raw can be specified."); + exit(0); + } + (None, None) => { if atty::is(atty::Stream::Stdin) { log::error!("No input file, source, or stdin to translate from."); exit(0); @@ -67,7 +77,11 @@ impl Translate { } /// render without handling svgs or anything -fn simple_render_child(f: &mut impl std::fmt::Write, child: &Node, il: u32) -> std::fmt::Result { +pub fn simple_render_child( + f: &mut impl std::fmt::Write, + child: &Node, + il: u32, +) -> std::fmt::Result { write_tabs(f, il)?; match child { Node::Text(t) => writeln!(f, "\"{}\"", t)?, diff --git a/tests/test.html b/tests/test.html index 4936ae36a..05ccd7b18 100644 --- a/tests/test.html +++ b/tests/test.html @@ -1,32 +1,48 @@
-
-
-
-
- content -
-

Buy YouTube Videos

-

- Williamsburg occupy sustainable snackwave gochujang. Pinterest cornhole brunch, slow-carb neutra - irony. -

- -
-
-
- content -
-

The Catalyzer

-

- Williamsburg occupy sustainable snackwave gochujang. Pinterest - cornhole brunch, slow-carb neutra irony. -

- -
+
+
+
+
+ content
+

+ Buy YouTube Videos +

+

+ Williamsburg occupy sustainable snackwave gochujang. Pinterest + cornhole brunch, slow-carb neutra irony. +

+ +
+
+
+ content +
+

+ The Catalyzer +

+

+ Williamsburg occupy sustainable snackwave gochujang. Pinterest + cornhole brunch, slow-carb neutra irony. +

+ +
+
From 84f0245f6c5fe18412b799e9019327fb73788083 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 24 Apr 2022 03:24:41 -0400 Subject: [PATCH 100/516] feat: basic autofmt --- Cargo.toml | 3 ++ extension/DEV.md | 10 +++++ extension/README.md | 10 ----- extension/package.json | 14 ++++-- extension/src/extension.ts | 86 ++++++++++++++++++++++++++----------- extension/static/icon.png | Bin 0 -> 20768 bytes src/cli/autoformat/mod.rs | 25 +++++++++++ src/cli/mod.rs | 11 +++++ src/main.rs | 6 +++ 9 files changed, 127 insertions(+), 38 deletions(-) create mode 100644 extension/DEV.md create mode 100644 extension/static/icon.png create mode 100644 src/cli/autoformat/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 21e2dad7e..b8f0e271b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,9 @@ flate2 = "1.0.22" tar = "0.4.38" tower = "0.4.12" +dioxus-rsx = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } +dioxus-autofmt = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } + [[bin]] path = "src/main.rs" name = "dioxus" diff --git a/extension/DEV.md b/extension/DEV.md new file mode 100644 index 000000000..54d0c396a --- /dev/null +++ b/extension/DEV.md @@ -0,0 +1,10 @@ + +## packaging + +``` +$ cd myExtension +$ vsce package +# myExtension.vsix generated +$ vsce publish +# .myExtension published to VS Code Marketplace +``` diff --git a/extension/README.md b/extension/README.md index bf9459fe9..fe67f1587 100644 --- a/extension/README.md +++ b/extension/README.md @@ -12,13 +12,3 @@ Converts a selection of html to valid rsx. ### Convert HTML to Dioxus Component Converts a selection of html to a valid Dioxus component with all SVGs factored out into their own module. - -## packaging - -``` -$ cd myExtension -$ vsce package -# myExtension.vsix generated -$ vsce publish -# .myExtension published to VS Code Marketplace -``` diff --git a/extension/package.json b/extension/package.json index c492f97e2..ca73c0ea8 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,14 +1,15 @@ { - "name": "dioxusstudio", + "name": "dioxus", "displayName": "Dioxus", "description": "Useful tools for working with Dioxus", "version": "0.0.1", - "publisher": "jkelleyrtp", + "publisher": "DioxusLabs", "private": true, "license": "MIT", + "icon": "static/icon.png", "repository": { "type": "git", - "url": "https://github.com/Microsoft/vscode-extension-samples" + "url": "https://github.com/DioxusLabs/cli" }, "engines": { "vscode": "^1.32.0" @@ -18,7 +19,8 @@ ], "activationEvents": [ "onCommand:extension.htmlToDioxusRsx", - "onCommand:extension.htmlToDioxusComponent" + "onCommand:extension.htmlToDioxusComponent", + "onCommand:extension.formatRsx" ], "main": "./out/extension", "contributes": { @@ -30,6 +32,10 @@ { "command": "extension.htmlToDioxusComponent", "title": "Dioxus: Convert HTML to Component" + }, + { + "command": "extension.formatRsx", + "title": "Dioxus: Format RSX" } ] }, diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 4322c48e1..fd8e2906b 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -3,33 +3,71 @@ import { spawn } from "child_process"; export function activate(context: vscode.ExtensionContext) { - function registerCommand(cmd: string) { - function convert(cmd: string) { - const editor = vscode.window.activeTextEditor;// Get the active text editor - if (editor) { - const html = editor.document.getText(editor.selection); - if (html.length > 0) { - let params = ["translate"]; - if (cmd.includes("Component")) params.push("--component"); - params.push("--raw"); - params.push(html); - const child_proc = spawn("dioxus", params); - let result = ''; - child_proc.stdout?.on('data', data => result += data); - child_proc.on('close', () => { - if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); - }); - } else { - vscode.window.showWarningMessage("Please select HTML fragment before invoking this command!"); - } - } + + function translate(component: boolean) { + const editor = vscode.window.activeTextEditor;// Get the active text editor + if (!editor) return; + + const html = editor.document.getText(editor.selection); + if (html.length == 0) { + vscode.window.showWarningMessage("Please select HTML fragment before invoking this command!"); + return; } + let params = ["translate"]; + if (component) params.push("--component"); + params.push("--raw"); + params.push(html); - const handle = vscode.commands.registerCommand(cmd, () => convert(cmd)); - context.subscriptions.push(handle); + const child_proc = spawn("dioxus", params); + + let result = ''; + child_proc.stdout?.on('data', data => result += data); + + child_proc.on('close', () => { + if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); } - registerCommand('extension.htmlToDioxusRsx'); - registerCommand('extension.htmlToDioxusComponent'); + function autoformat() { + const editor = vscode.window.activeTextEditor;// Get the active text editor + if (!editor) return; + + const unformatted = editor.document.getText(editor.selection); + if (unformatted.length == 0) { + vscode.window.showWarningMessage("Please select rsx invoking this command!"); + return; + } + + const params = ["fmt", "--raw", unformatted]; + + const child_proc = spawn("dioxus", params); + + let result = ''; + child_proc.stdout?.on('data', data => result += data); + + child_proc.on('close', () => { + if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); + } + + + + + const handles = + [ + vscode.commands.registerCommand('extension.htmlToDioxusRsx', () => translate(false)), + vscode.commands.registerCommand('extension.htmlToDioxusComponent', () => translate(true)), + vscode.commands.registerCommand('extension.formatRsx', () => autoformat()) + ]; + + context.subscriptions.push(...handles); } diff --git a/extension/static/icon.png b/extension/static/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..42c355c2647b0b68cf18c12d292db134ac8750d2 GIT binary patch literal 20768 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 IM6N<$f;9DK`~Uy| literal 0 HcmV?d00001 diff --git a/src/cli/autoformat/mod.rs b/src/cli/autoformat/mod.rs new file mode 100644 index 000000000..26ab86345 --- /dev/null +++ b/src/cli/autoformat/mod.rs @@ -0,0 +1,25 @@ +use super::*; + +/// Build the Rust WASM app and all of its assets. +#[derive(Clone, Debug, Parser)] +pub struct Autoformat { + /// Input file + #[clap(short, long)] + pub raw: Option, +} + +impl Autoformat { + pub fn autoformat(self) -> Result<()> { + if let Some(raw) = self.raw { + if let Some(inner) = dioxus_autofmt::fmt_block(&raw) { + println!("{}", inner); + } else { + // exit process with error + eprintln!("error formatting codeblock"); + exit(1); + } + } + + Ok(()) + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d639d28a7..92e05eaa9 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,3 +1,4 @@ +pub mod autoformat; pub mod build; pub mod cfg; pub mod clean; @@ -45,17 +46,27 @@ pub struct Cli { 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), + /// Init a new project for Dioxus. Create(create::Create), + /// Clean output artifacts. Clean(clean::Clean), + + /// Format some rsx + #[clap(name = "fmt")] + Autoformat(autoformat::Autoformat), + /// Dioxus config file controls. #[clap(subcommand)] Config(config::Config), + /// Install & Manage tools for Dioxus-cli. #[clap(subcommand)] Tool(tool::Tool), diff --git a/src/main.rs b/src/main.rs index 099ff74c0..72558741e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,12 @@ async fn main() -> Result<()> { log::error!("tool error: {}", e); } } + + Commands::Autoformat(opts) => { + if let Err(e) = opts.autoformat() { + log::error!("format error: {}", e); + } + } } Ok(()) From 201dcc144d363571865ace40d85336b3c792d474 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 26 Apr 2022 18:45:27 -0400 Subject: [PATCH 101/516] feat: fix bug with applying multiple edits in sequence --- Cargo.toml | 6 ++- extension/package.json | 7 +++- extension/src/extension.ts | 85 ++++++++++++++++++++++++++++++++------ src/cli/autoformat/mod.rs | 38 ++++++++++++++++- 4 files changed, 120 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b8f0e271b..9f85a89c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,8 +46,10 @@ flate2 = "1.0.22" tar = "0.4.38" tower = "0.4.12" -dioxus-rsx = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } -dioxus-autofmt = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } +dioxus-rsx = { path = "../../dioxus/packages/rsx" } +dioxus-autofmt = { path = "../../dioxus/packages/autofmt" } +# dioxus-rsx = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } +# dioxus-autofmt = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } [[bin]] path = "src/main.rs" diff --git a/extension/package.json b/extension/package.json index ca73c0ea8..f884637c8 100644 --- a/extension/package.json +++ b/extension/package.json @@ -20,7 +20,8 @@ "activationEvents": [ "onCommand:extension.htmlToDioxusRsx", "onCommand:extension.htmlToDioxusComponent", - "onCommand:extension.formatRsx" + "onCommand:extension.formatRsx", + "onCommand:extension.formatRsxDocument" ], "main": "./out/extension", "contributes": { @@ -36,6 +37,10 @@ { "command": "extension.formatRsx", "title": "Dioxus: Format RSX" + }, + { + "command": "extension.formatRsxDocument", + "title": "Dioxus: Format RSX Document" } ] }, diff --git a/extension/src/extension.ts b/extension/src/extension.ts index fd8e2906b..46ce2aa89 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -33,7 +33,7 @@ export function activate(context: vscode.ExtensionContext) { }); } - function autoformat() { + function autoformat_selection() { const editor = vscode.window.activeTextEditor;// Get the active text editor if (!editor) return; @@ -43,11 +43,10 @@ export function activate(context: vscode.ExtensionContext) { return; } - const params = ["fmt", "--raw", unformatted]; - - const child_proc = spawn("dioxus", params); - + let args = ["fmt", "--raw", unformatted.toString()]; + const child_proc = spawn("dioxus", args); let result = ''; + child_proc.stdout?.on('data', data => result += data); child_proc.on('close', () => { @@ -60,14 +59,76 @@ export function activate(context: vscode.ExtensionContext) { } + function autoformat_document(document: vscode.TextDocument) { + // check the settings to make sure format on save is configured + // const formatOnSave: string | undefined = vscode.workspace.getConfiguration('rust').get('formatOnSave'); + if (document.languageId === "rust" && document.uri.scheme === "file") { + const editor = vscode.window.activeTextEditor;// Get the active text editor + if (editor) { + const text = editor.document.getText(); - const handles = - [ - vscode.commands.registerCommand('extension.htmlToDioxusRsx', () => translate(false)), - vscode.commands.registerCommand('extension.htmlToDioxusComponent', () => translate(true)), - vscode.commands.registerCommand('extension.formatRsx', () => autoformat()) - ]; + console.error(text); - context.subscriptions.push(...handles); + const child_proc = spawn("dioxus", ["fmt", "-f", text]); + + let result = ''; + + child_proc.stdout?.on('data', data => result += data); + + type RsxEdit = { + formatted: string, + start: number, + end: number + } + + child_proc.on('close', () => { + // if (result.length > 0) { + // editor.edit(editBuilder => editBuilder.insert(new vscode.Position(0, 0), result)); + // } else { + // console.error("No result"); + // } + if (result.length > 0) { + let decoded: RsxEdit[] = JSON.parse(result); + + console.log("Decoded edits: ", decoded); + + editor.edit(editBuilder => { + + for (let edit of decoded) { + console.log("Handling Edit: ", edit); + + let start = document.positionAt(edit.start - 1); + let end = document.positionAt(edit.end + 1); + const range = new vscode.Range(start, end); + + editBuilder.replace(range, `{ ${edit.formatted} }`); + // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { + // }); + // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { + // console.log("Edit applied: ", could_be_applied); + // }); + } + }) + } + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); + } + } + } + + context.subscriptions.push( + vscode.commands.registerCommand('extension.htmlToDioxusRsx', () => translate(false)), + vscode.commands.registerCommand('extension.htmlToDioxusComponent', () => translate(true)), + vscode.commands.registerCommand('extension.formatRsx', () => autoformat_selection()), + vscode.commands.registerCommand('extension.formatRsxDocument', () => { + const editor = vscode.window.activeTextEditor;// Get the active text editor + if (!editor) return; + autoformat_document(editor.document); + }), + vscode.workspace.onDidSaveTextDocument(autoformat_document) + ); } diff --git a/src/cli/autoformat/mod.rs b/src/cli/autoformat/mod.rs index 26ab86345..3008411db 100644 --- a/src/cli/autoformat/mod.rs +++ b/src/cli/autoformat/mod.rs @@ -3,9 +3,13 @@ use super::*; /// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Parser)] pub struct Autoformat { - /// Input file + /// Input rsx (selection) #[clap(short, long)] pub raw: Option, + + /// Input file + #[clap(short, long)] + pub file: Option, } impl Autoformat { @@ -20,6 +24,38 @@ impl Autoformat { } } + if let Some(file) = self.file { + let edits = dioxus_autofmt::get_format_blocks(&file); + let as_json = serde_json::to_string(&edits).unwrap(); + println!("{}", as_json); + } + Ok(()) } } + +#[test] +fn spawn_properly() { + let out = Command::new("dioxus") + .args([ + "fmt", + "-f", + r#" +// + +rsx! { + + div {} +} + +// +// +// + + "#, + ]) + .output() + .expect("failed to execute process"); + + dbg!(out); +} From 4a54934c8ef34d98861223bcf5be244fa3922027 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 28 Apr 2022 23:59:20 -0400 Subject: [PATCH 102/516] fix: extension should respect blocks --- extension/src/extension.ts | 49 +++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 46ce2aa89..44d5b8f16 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -64,9 +64,10 @@ export function activate(context: vscode.ExtensionContext) { // const formatOnSave: string | undefined = vscode.workspace.getConfiguration('rust').get('formatOnSave'); if (document.languageId === "rust" && document.uri.scheme === "file") { - const editor = vscode.window.activeTextEditor;// Get the active text editor - if (editor) { - const text = editor.document.getText(); + const active_editor = vscode.window.activeTextEditor;// Get the active text editor + + if (active_editor?.document.fileName === document.fileName) { + const text = document.getText(); console.error(text); @@ -83,33 +84,37 @@ export function activate(context: vscode.ExtensionContext) { } child_proc.on('close', () => { - // if (result.length > 0) { - // editor.edit(editBuilder => editBuilder.insert(new vscode.Position(0, 0), result)); - // } else { - // console.error("No result"); - // } + if (result.length > 0) { let decoded: RsxEdit[] = JSON.parse(result); console.log("Decoded edits: ", decoded); - editor.edit(editBuilder => { + // Get the active text editor + const cur_editor = vscode.window.activeTextEditor; - for (let edit of decoded) { - console.log("Handling Edit: ", edit); - let start = document.positionAt(edit.start - 1); - let end = document.positionAt(edit.end + 1); - const range = new vscode.Range(start, end); + // even if we build an empty edit stream, it tries to save the editor + // this makes sure the editor is actually saved + if (decoded.length > 0 && cur_editor == active_editor) { + active_editor.edit(editBuilder => { - editBuilder.replace(range, `{ ${edit.formatted} }`); - // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { - // }); - // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { - // console.log("Edit applied: ", could_be_applied); - // }); - } - }) + for (let edit of decoded) { + console.log("Handling Edit: ", edit); + + let start = document.positionAt(edit.start - 1); + let end = document.positionAt(edit.end + 1); + const range = new vscode.Range(start, end); + + editBuilder.replace(range, `{ ${edit.formatted} }`); + // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { + // }); + // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { + // console.log("Edit applied: ", could_be_applied); + // }); + } + }) + } } }); From 2c9f5f1957941dd7875f0b01742809f9ec0823f1 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Fri, 29 Apr 2022 12:59:15 +0800 Subject: [PATCH 103/516] Fixed `dioxus.toml` --- src/assets/dioxus.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index ab85abe86..b43d0d6df 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -42,4 +42,4 @@ script = [] # use binaryen.wasm-opt for output Wasm file # binaryen just will trigger in `web` platform -binaryen = { wasm_opt = [".PROJECT_OUT"] } \ No newline at end of file +binaryen = { wasm_opt = true } \ No newline at end of file From 5d013ecf824b1e62b8741e7e4565af8bf3262f97 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 7 May 2022 11:54:00 +0800 Subject: [PATCH 104/516] Try to add `Sass` support --- src/assets/dioxus.toml | 5 ++++- src/tools.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index b43d0d6df..0c57542b8 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/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 } + +# use sass auto will auto check sass file and build it. +sass = { auto = true } \ No newline at end of file diff --git a/src/tools.rs b/src/tools.rs index 937063be0..db4d82268 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -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 { @@ -60,6 +61,7 @@ impl Tool { pub fn name(&self) -> &str { match self { Self::Binaryen => "binaryen", + Self::Sass => "sass", } } @@ -67,6 +69,7 @@ impl Tool { pub fn bin_path(&self) -> &str { match self { Self::Binaryen => "bin", + Self::Sass => ".", } } @@ -84,6 +87,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 +110,12 @@ 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.tar.gz", + target = self.target_platform() + ) + } } } @@ -103,6 +123,13 @@ impl Tool { pub fn extension(&self) -> &str { match self { Self::Binaryen => "tar.gz", + Self::Sass => { + if cfg!(target_os = "windows") { + "zip" + } else { + "tar.ge" + } + }, } } @@ -169,6 +196,9 @@ impl Tool { command.to_string() } } + Tool::Sass => { + command.to_string() + } }; if !bin_path.join(&command_file).is_file() { From c48345b86e556c0535d4ead6134921c68d4c121c Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 7 May 2022 16:25:23 +0800 Subject: [PATCH 105/516] Add `zip` support --- Cargo.toml | 2 ++ src/tools.rs | 46 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 21e2dad7e..d2bda881f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,11 +39,13 @@ 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" + # 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" [[bin]] diff --git a/src/tools.rs b/src/tools.rs index db4d82268..b37d10cd2 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,6 +1,6 @@ use std::{ fs::{create_dir_all, File}, - path::PathBuf, + path::{Path, PathBuf}, process::Command, }; @@ -112,8 +112,9 @@ impl Tool { } Self::Sass => { format!( - "https://github.com/sass/dart-sass/releases/download/1.51.0/dart-sass-1.51.0-{target}-x64.tar.gz", - target = self.target_platform() + "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() ) } } @@ -129,7 +130,7 @@ impl Tool { } else { "tar.ge" } - }, + } } } @@ -169,6 +170,8 @@ impl Tool { let dir_name = if self == &Tool::Binaryen { "binaryen-version_105" + } else if self == &Tool::Sass { + "sass-version-1_51" } else { "" }; @@ -180,6 +183,8 @@ impl Tool { 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 } Ok(()) @@ -196,9 +201,7 @@ impl Tool { command.to_string() } } - Tool::Sass => { - command.to_string() - } + Tool::Sass => command.to_string(), }; if !bin_path.join(&command_file).is_file() { @@ -215,3 +218,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(()) +} From ea723f64cd362d4139d0766fc5a74b95166e2ca0 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 7 May 2022 19:11:12 +0800 Subject: [PATCH 106/516] Support `sass` install --- src/cli/tool/mod.rs | 4 ++-- src/tools.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs index d5c4581aa..f13c1620e 100644 --- a/src/cli/tool/mod.rs +++ b/src/cli/tool/mod.rs @@ -20,9 +20,9 @@ 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}"); } } } diff --git a/src/tools.rs b/src/tools.rs index b37d10cd2..9f96ce0b9 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -53,6 +53,7 @@ impl Tool { pub fn from_str(name: &str) -> Option { match name { "binaryen" => Some(Self::Binaryen), + "sass" => Some(Self::Sass), _ => None, } } @@ -128,7 +129,7 @@ impl Tool { if cfg!(target_os = "windows") { "zip" } else { - "tar.ge" + "tar.gz" } } } @@ -159,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) } @@ -170,10 +171,8 @@ impl Tool { let dir_name = if self == &Tool::Binaryen { "binaryen-version_105" - } else if self == &Tool::Sass { - "sass-version-1_51" } else { - "" + "dart-sass" }; if self.extension() == "tar.gz" { @@ -181,10 +180,11 @@ 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()))?; } Ok(()) From f7f1d90520bf9712172175ad6ccbec41803172f2 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sun, 8 May 2022 00:56:22 +0800 Subject: [PATCH 107/516] Add `sass` support --- src/assets/dioxus.toml | 14 +++++++++++++- src/cli/tool/mod.rs | 7 +++++-- src/tools.rs | 8 +++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 0c57542b8..8f160b293 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -45,4 +45,16 @@ script = [] binaryen = { wasm_opt = true } # use sass auto will auto check sass file and build it. -sass = { auto = true } \ No newline at end of file +[application.tools.sass] + +version = "1.51.0" + +# if auto = true, we will check style array, and translate all sass file to css. +auto = true + +# or you can specify some sass file -> css file +files = [ + # some sass file path + # this file will translate to `/css/test.css` + "/css/test.sass" +] \ No newline at end of file diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs index f13c1620e..45c866c40 100644 --- a/src/cli/tool/mod.rs +++ b/src/cli/tool/mod.rs @@ -27,7 +27,11 @@ impl Tool { } } 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(()) } } diff --git a/src/tools.rs b/src/tools.rs index 9f96ce0b9..ae4e58b79 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -201,7 +201,13 @@ impl Tool { command.to_string() } } - Tool::Sass => 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() { From 768a0426d8519073259f9e642bc0b44f75110f52 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Tue, 10 May 2022 10:31:02 +0800 Subject: [PATCH 108/516] Add `sass` support --- Cargo.toml | 2 ++ src/assets/dioxus.toml | 4 +--- src/builder.rs | 48 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d2bda881f..c869267b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,8 @@ 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"] } diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 8f160b293..3e82e0c93 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -47,8 +47,6 @@ binaryen = { wasm_opt = true } # use sass auto will auto check sass file and build it. [application.tools.sass] -version = "1.51.0" - # if auto = true, we will check style array, and translate all sass file to css. auto = true @@ -56,5 +54,5 @@ auto = true files = [ # some sass file path # this file will translate to `/css/test.css` - "/css/test.sass" + "/css/test.scss" ] \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index f7f68e2bc..7c3a7e9fb 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,6 +1,7 @@ use crate::{ config::{CrateConfig, ExecutableType}, error::{Error, Result}, + tools::Tool, DioxusConfig, }; use std::{ @@ -29,6 +30,10 @@ pub fn build(config: &CrateConfig) -> Result<()> { .. } = config; + + // start to build the assets + build_assets(config)?; + let t_start = std::time::Instant::now(); // [1] Build the .wasm module @@ -339,6 +344,49 @@ 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 +fn build_assets(config: &CrateConfig) -> Result<()> { + 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() { + if tab.contains_key("auto") && tab.get("auto").unwrap().as_bool().unwrap_or(false) { + // 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.file_name().unwrap().to_str().unwrap(); + let suffix = suffix.split('.').collect::>(); + let suffix: &str = suffix.last().unwrap(); + if suffix == "scss" { + // if file suffix is `scss` we need transform it. + let temp_stem = temp.file_stem().unwrap().to_str().unwrap(); + let target_path = + temp.parent().unwrap().join(format!("{}.css", temp_stem)); + sass.call( + "sass", + vec![temp.to_str().unwrap(), target_path.to_str().unwrap()], + )?; + } + } + } + } + } + } + } + Ok(()) +} + // use binary_install::{Cache, Download}; // /// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a From 8b06fe60596371118e47ef71a72627044c17672a Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Tue, 10 May 2022 13:46:53 +0800 Subject: [PATCH 109/516] Fixed sass tool --- src/assets/dioxus.toml | 4 ++-- src/builder.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 3e82e0c93..105251aa1 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -47,10 +47,10 @@ binaryen = { wasm_opt = true } # use sass auto will auto check sass file and build it. [application.tools.sass] -# if auto = true, we will check style array, and translate all sass file to css. +# auto will check the assets dirs, and auto to transform all scss file to css file. auto = true -# or you can specify some sass file -> css file +# you can specify some scss file -> css file files = [ # some sass file path # this file will translate to `/css/test.css` diff --git a/src/builder.rs b/src/builder.rs index 7c3a7e9fb..0e0cf7e9a 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -30,7 +30,6 @@ pub fn build(config: &CrateConfig) -> Result<()> { .. } = config; - // start to build the assets build_assets(config)?; @@ -359,20 +358,21 @@ fn build_assets(config: &CrateConfig) -> Result<()> { // 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) + 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.file_name().unwrap().to_str().unwrap(); - let suffix = suffix.split('.').collect::>(); - let suffix: &str = suffix.last().unwrap(); + let suffix = temp.extension().unwrap().to_str().unwrap(); if suffix == "scss" { // if file suffix is `scss` we need transform it. - let temp_stem = temp.file_stem().unwrap().to_str().unwrap(); - let target_path = - temp.parent().unwrap().join(format!("{}.css", temp_stem)); + 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); sass.call( "sass", vec![temp.to_str().unwrap(), target_path.to_str().unwrap()], From b5ecd09185062724415e9bf7b6b10d7e2c6860e8 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Tue, 10 May 2022 19:22:19 +0800 Subject: [PATCH 110/516] Add `files` list option --- src/assets/dioxus.toml | 2 +- src/builder.rs | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 105251aa1..5f5032e2a 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -50,7 +50,7 @@ binaryen = { wasm_opt = true } # auto will check the assets dirs, and auto to transform all scss file to css file. auto = true -# you can specify some scss file -> css file +# or you can specify some scss file -> css file files = [ # some sass file path # this file will translate to `/css/test.css` diff --git a/src/builder.rs b/src/builder.rs index 0e0cf7e9a..fb0c8088d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -381,6 +381,28 @@ fn build_assets(config: &CrateConfig) -> Result<()> { } } } + } else if tab.contains_key("files") && tab.get("files").unwrap().is_array() { + // check files list. + let list = tab.get("files").unwrap().as_array().unwrap(); + for i in list { + if i.is_str() { + let path = i.as_str().unwrap(); + let path = PathBuf::from(path); + let path = config.asset_dir.join(path); + if path.is_file() { + sass.call( + "sass", + vec![ + path.to_str().unwrap(), + path.parent().unwrap().join(format!( + "{}.css", + path.file_stem().unwrap().to_str().unwrap() + )).to_str().unwrap(), + ], + )?; + } + } + } } } } From 551d2477ffcd84d13cfc4c5d15e1c8033a23a160 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Tue, 10 May 2022 22:32:47 +0800 Subject: [PATCH 111/516] Add `source_map` option --- src/assets/dioxus.toml | 4 +++- src/builder.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 5f5032e2a..b481193a8 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -55,4 +55,6 @@ files = [ # some sass file path # this file will translate to `/css/test.css` "/css/test.scss" -] \ No newline at end of file +] + +source_map = true \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index fb0c8088d..825a9eebf 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -354,6 +354,17 @@ fn build_assets(config: &CrateConfig) -> Result<()> { 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 { + "--no-source-map" + }; + if tab.contains_key("auto") && tab.get("auto").unwrap().as_bool().unwrap_or(false) { // if the sass open auto, we need auto-check the assets dir. let asset_dir = config.asset_dir.clone(); @@ -375,7 +386,11 @@ fn build_assets(config: &CrateConfig) -> Result<()> { .join(out_file); sass.call( "sass", - vec![temp.to_str().unwrap(), target_path.to_str().unwrap()], + vec![ + temp.to_str().unwrap(), + target_path.to_str().unwrap(), + source_map, + ], )?; } } @@ -394,10 +409,15 @@ fn build_assets(config: &CrateConfig) -> Result<()> { "sass", vec![ path.to_str().unwrap(), - path.parent().unwrap().join(format!( - "{}.css", - path.file_stem().unwrap().to_str().unwrap() - )).to_str().unwrap(), + path.parent() + .unwrap() + .join(format!( + "{}.css", + path.file_stem().unwrap().to_str().unwrap() + )) + .to_str() + .unwrap(), + source_map, ], )?; } From 9ddaa773d9ffa8d6274a2731dfdac00223f6b74d Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Tue, 10 May 2022 22:58:48 +0800 Subject: [PATCH 112/516] Remove `auto` option --- src/builder.rs | 111 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 38 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 825a9eebf..75c83990d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -362,48 +362,54 @@ fn build_assets(config: &CrateConfig) -> Result<()> { "--no-source-map" } } else { - "--no-source-map" + "--source-map" }; - if tab.contains_key("auto") && tab.get("auto").unwrap().as_bool().unwrap_or(false) { - // 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().unwrap().to_str().unwrap(); - if suffix == "scss" { - // if file suffix is `scss` 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); - sass.call( - "sass", - vec![ - temp.to_str().unwrap(), - target_path.to_str().unwrap(), - source_map, - ], - )?; + if tab.contains_key("files") { + if tab.get("files").unwrap().is_str() { + let file = tab.get("files").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().unwrap().to_str().unwrap(); + if suffix == "scss" { + // if file suffix is `scss` 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); + sass.call( + "sass", + vec![ + temp.to_str().unwrap(), + target_path.to_str().unwrap(), + source_map, + ], + )?; + } + } } } - } - } - } else if tab.contains_key("files") && tab.get("files").unwrap().is_array() { - // check files list. - let list = tab.get("files").unwrap().as_array().unwrap(); - for i in list { - if i.is_str() { - let path = i.as_str().unwrap(); - let path = PathBuf::from(path); - let path = config.asset_dir.join(path); + } else { + // just transform one file. + let path = config.asset_dir.join(file); if path.is_file() { sass.call( "sass", @@ -422,10 +428,39 @@ fn build_assets(config: &CrateConfig) -> Result<()> { )?; } } + } else if tab.get("files").unwrap().is_array() { + // check files list. + let list = tab.get("files").unwrap().as_array().unwrap(); + for i in list { + if i.is_str() { + let path = i.as_str().unwrap(); + let path = PathBuf::from(path); + let path = config.asset_dir.join(path); + if path.is_file() { + sass.call( + "sass", + vec![ + path.to_str().unwrap(), + path.parent() + .unwrap() + .join(format!( + "{}.css", + path.file_stem().unwrap().to_str().unwrap() + )) + .to_str() + .unwrap(), + source_map, + ], + )?; + } + } + } } } } } + // SASS END + Ok(()) } From ecb846dd24d34c0827bfa3503048bf97a8389fda Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Tue, 10 May 2022 23:00:39 +0800 Subject: [PATCH 113/516] Rename `files` to `input` --- src/assets/dioxus.toml | 12 ++++++------ src/builder.rs | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index b481193a8..a5e849bfb 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -48,13 +48,13 @@ binaryen = { wasm_opt = true } [application.tools.sass] # auto will check the assets dirs, and auto to transform all scss file to css file. -auto = true +input = "*" # or you can specify some scss file -> css file -files = [ - # some sass file path - # this file will translate to `/css/test.css` - "/css/test.scss" -] +# input = [ +# # some sass file path +# # this file will translate to `/css/test.css` +# "/css/test.scss" +# ] source_map = true \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 75c83990d..f9f0bd9bd 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -365,9 +365,9 @@ fn build_assets(config: &CrateConfig) -> Result<()> { "--source-map" }; - if tab.contains_key("files") { - if tab.get("files").unwrap().is_str() { - let file = tab.get("files").unwrap().as_str().unwrap().trim(); + 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. @@ -428,9 +428,9 @@ fn build_assets(config: &CrateConfig) -> Result<()> { )?; } } - } else if tab.get("files").unwrap().is_array() { + } else if tab.get("input").unwrap().is_array() { // check files list. - let list = tab.get("files").unwrap().as_array().unwrap(); + let list = tab.get("input").unwrap().as_array().unwrap(); for i in list { if i.is_str() { let path = i.as_str().unwrap(); From bf49d526d773cc87449ac343659609c52890f0ac Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 00:27:37 +0800 Subject: [PATCH 114/516] Fixed some problem --- src/builder.rs | 13 ++++++++++++- src/tools.rs | 6 +++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index f9f0bd9bd..77f0838f0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -409,7 +409,13 @@ fn build_assets(config: &CrateConfig) -> Result<()> { } } else { // just transform one file. - let path = config.asset_dir.join(file); + let path = if &file[0..] == "/" { + &file[1..file.len() - 1] + } else { + file + }; + let path = PathBuf::from(path); + let path = config.asset_dir.join(path); if path.is_file() { sass.call( "sass", @@ -434,6 +440,11 @@ fn build_assets(config: &CrateConfig) -> Result<()> { for i in list { if i.is_str() { let path = i.as_str().unwrap(); + let path = if &path[0..] == "/" { + &path[1..path.len() - 1] + } else { + path + }; let path = PathBuf::from(path); let path = config.asset_dir.join(path); if path.is_file() { diff --git a/src/tools.rs b/src/tools.rs index ae4e58b79..2952a9614 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -207,7 +207,7 @@ impl Tool { } else { command.to_string() } - }, + } }; if !bin_path.join(&command_file).is_file() { @@ -232,7 +232,7 @@ fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { 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() { @@ -252,4 +252,4 @@ fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { } Ok(()) -} +} \ No newline at end of file From c6aa4e332df58c223416fafc62e729574ff8c564 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 10:01:02 +0800 Subject: [PATCH 115/516] Ignore `scss` file in out_dir --- src/builder.rs | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 77f0838f0..25d087191 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -31,7 +31,7 @@ pub fn build(config: &CrateConfig) -> Result<()> { } = config; // start to build the assets - build_assets(config)?; + let ignore_files = build_assets(config)?; let t_start = std::time::Instant::now(); @@ -158,6 +158,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)?; + } + } } } } @@ -345,7 +352,11 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { // this function will build some assets file // like sass tool resources -fn build_assets(config: &CrateConfig) -> Result<()> { +// this function will return a array which file don't need copy to out_dir. +fn build_assets(config: &CrateConfig) -> Result> { + + let mut result = vec![]; + let dioxus_config = &config.dioxus_config; let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default(); @@ -395,14 +406,17 @@ fn build_assets(config: &CrateConfig) -> Result<()> { .unwrap(), ) .join(out_file); - sass.call( + 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()); + } } } } @@ -417,7 +431,7 @@ fn build_assets(config: &CrateConfig) -> Result<()> { let path = PathBuf::from(path); let path = config.asset_dir.join(path); if path.is_file() { - sass.call( + let res = sass.call( "sass", vec![ path.to_str().unwrap(), @@ -431,7 +445,10 @@ fn build_assets(config: &CrateConfig) -> Result<()> { .unwrap(), source_map, ], - )?; + ); + if res.is_ok() { + result.push(path); + } } } } else if tab.get("input").unwrap().is_array() { @@ -448,7 +465,7 @@ fn build_assets(config: &CrateConfig) -> Result<()> { let path = PathBuf::from(path); let path = config.asset_dir.join(path); if path.is_file() { - sass.call( + let res = sass.call( "sass", vec![ path.to_str().unwrap(), @@ -462,7 +479,10 @@ fn build_assets(config: &CrateConfig) -> Result<()> { .unwrap(), source_map, ], - )?; + ); + if res.is_ok() { + result.push(path); + } } } } @@ -472,7 +492,7 @@ fn build_assets(config: &CrateConfig) -> Result<()> { } // SASS END - Ok(()) + Ok(result) } // use binary_install::{Cache, Download}; From dccd82091a0b499527cd435fccf1a088262235e1 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 11:12:23 +0800 Subject: [PATCH 116/516] Fixed `input` option error --- src/builder.rs | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 25d087191..e1ed0a8f1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -354,7 +354,6 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { // 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> { - let mut result = vec![]; let dioxus_config = &config.dioxus_config; @@ -430,19 +429,23 @@ fn build_assets(config: &CrateConfig) -> Result> { }; let path = PathBuf::from(path); let path = config.asset_dir.join(path); + let out_file = + format!("{}.css", path.file_stem().unwrap().to_str().unwrap()); + let target_path = config + .out_dir + .join( + path.strip_prefix(&config.asset_dir) + .unwrap() + .parent() + .unwrap(), + ) + .join(out_file); if path.is_file() { let res = sass.call( "sass", vec![ path.to_str().unwrap(), - path.parent() - .unwrap() - .join(format!( - "{}.css", - path.file_stem().unwrap().to_str().unwrap() - )) - .to_str() - .unwrap(), + target_path.to_str().unwrap(), source_map, ], ); @@ -464,19 +467,23 @@ fn build_assets(config: &CrateConfig) -> Result> { }; let path = PathBuf::from(path); let path = config.asset_dir.join(path); + let out_file = + format!("{}.css", path.file_stem().unwrap().to_str().unwrap()); + let target_path = config + .out_dir + .join( + path.strip_prefix(&config.asset_dir) + .unwrap() + .parent() + .unwrap(), + ) + .join(out_file); if path.is_file() { let res = sass.call( "sass", vec![ path.to_str().unwrap(), - path.parent() - .unwrap() - .join(format!( - "{}.css", - path.file_stem().unwrap().to_str().unwrap() - )) - .to_str() - .unwrap(), + target_path.to_str().unwrap(), source_map, ], ); From 065da8db6589a1c745392b3973bae127ebecb183 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 11:14:49 +0800 Subject: [PATCH 117/516] Add `sass` desktop build support --- src/builder.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index e1ed0a8f1..ae6e83605 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -177,6 +177,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") @@ -261,6 +263,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)?; + } + } } } } From 60bb0d9eb9f6a6e10267df85840914afa04d460b Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 11:15:33 +0800 Subject: [PATCH 118/516] Format code --- src/cli/tool/mod.rs | 2 +- src/tools.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/tool/mod.rs b/src/cli/tool/mod.rs index 45c866c40..fde94b29a 100644 --- a/src/cli/tool/mod.rs +++ b/src/cli/tool/mod.rs @@ -27,7 +27,7 @@ impl Tool { } } Tool::AppPath {} => { - if let Some(v) = tools::tools_path().to_str() { + if let Some(v) = tools::tools_path().to_str() { println!("{}", v); } else { log::error!("Tools path get failed."); diff --git a/src/tools.rs b/src/tools.rs index 2952a9614..2ab358d6b 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -252,4 +252,4 @@ fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { } Ok(()) -} \ No newline at end of file +} From cb650bad8a726c50eacdf8d29669532a5fb75c42 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 11:47:49 +0800 Subject: [PATCH 119/516] Fixed some problem --- src/builder.rs | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index ae6e83605..8f3823a65 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -431,24 +431,19 @@ fn build_assets(config: &CrateConfig) -> Result> { } } else { // just transform one file. - let path = if &file[0..] == "/" { - &file[1..file.len() - 1] + let relative_path = if &file[0..1] == "/" { + &file[1..file.len()] } else { file }; - let path = PathBuf::from(path); - let path = config.asset_dir.join(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( - path.strip_prefix(&config.asset_dir) - .unwrap() - .parent() - .unwrap(), - ) + .join(PathBuf::from(relative_path).parent().unwrap()) .join(out_file); + println!("{:?} -- {:?}", target_path, path); if path.is_file() { let res = sass.call( "sass", @@ -460,6 +455,8 @@ fn build_assets(config: &CrateConfig) -> Result> { ); if res.is_ok() { result.push(path); + } else { + log::error!("{:?}", res); } } } @@ -469,23 +466,17 @@ fn build_assets(config: &CrateConfig) -> Result> { for i in list { if i.is_str() { let path = i.as_str().unwrap(); - let path = if &path[0..] == "/" { - &path[1..path.len() - 1] + let relative_path = if &path[0..1] == "/" { + &path[1..path.len()] } else { path }; - let path = PathBuf::from(path); - let path = config.asset_dir.join(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( - path.strip_prefix(&config.asset_dir) - .unwrap() - .parent() - .unwrap(), - ) + .join(PathBuf::from(relative_path).parent().unwrap()) .join(out_file); if path.is_file() { let res = sass.call( From 7f829c4553eaf45eda2e9c7964ba28cac63723cb Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 11 May 2022 11:50:01 +0800 Subject: [PATCH 120/516] Format code --- src/builder.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 8f3823a65..e0d3ac285 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -443,7 +443,6 @@ fn build_assets(config: &CrateConfig) -> Result> { .out_dir .join(PathBuf::from(relative_path).parent().unwrap()) .join(out_file); - println!("{:?} -- {:?}", target_path, path); if path.is_file() { let res = sass.call( "sass", From cc56dd19ddbdaf9578ee6f56df7a354bbd2d3bc6 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Thu, 12 May 2022 00:11:51 +0800 Subject: [PATCH 121/516] Add `sass` file extension --- src/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index e0d3ac285..cdb431933 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -399,8 +399,8 @@ fn build_assets(config: &CrateConfig) -> Result> { let temp = entry.path(); if temp.is_file() { let suffix = temp.extension().unwrap().to_str().unwrap(); - if suffix == "scss" { - // if file suffix is `scss` we need transform it. + 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() From 87e6ed0a3d13dfb023c185a249ceee3a7a22d9e8 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Thu, 12 May 2022 22:49:27 +0800 Subject: [PATCH 122/516] Fixed builder problem --- src/builder.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index cdb431933..4f6824780 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -398,7 +398,9 @@ fn build_assets(config: &CrateConfig) -> Result> { { let temp = entry.path(); if temp.is_file() { - let suffix = temp.extension().unwrap().to_str().unwrap(); + 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!( From 51639af48230bb8a4bb7b50105c1bcd270ac0774 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Mon, 6 Jun 2022 00:16:15 +0800 Subject: [PATCH 123/516] feat: add `profile` flag --- src/builder.rs | 5 +++++ src/cli/build/mod.rs | 4 ++++ src/cli/cfg.rs | 8 ++++++++ src/cli/serve/mod.rs | 4 ++++ src/config.rs | 9 +++++++++ 5 files changed, 30 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index 4f6824780..31bb96f46 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -49,6 +49,11 @@ pub fn build(config: &CrateConfig) -> Result<()> { cmd.arg("--release"); } + if config.custom_profile.is_some() { + let custom_profile = config.custom_profile.as_ref().unwrap(); + cmd.arg(format!("--profile {}", custom_profile)); + } + match executable { ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 4c043091f..7f8c7b420 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -19,6 +19,10 @@ impl Build { 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 diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index f5ee45b87..11a1603c7 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -16,6 +16,10 @@ pub struct ConfigOptsBuild { #[clap(long)] pub example: Option, + /// Build with custom profile + #[clap(long)] + pub profile: Option, + /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long)] pub platform: Option, @@ -36,6 +40,10 @@ pub struct ConfigOptsServe { #[serde(default)] pub release: bool, + /// Build with custom profile + #[clap(long)] + pub profile: Option, + /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long)] pub platform: Option, diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 85bd92c74..1b79284b7 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -24,6 +24,10 @@ impl Serve { 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 diff --git a/src/config.rs b/src/config.rs index 783c72ccd..9679fadaa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -114,6 +114,7 @@ pub struct CrateConfig { pub executable: ExecutableType, pub dioxus_config: DioxusConfig, pub release: bool, + pub custom_profile: Option, } #[derive(Debug, Clone)] @@ -163,6 +164,8 @@ impl CrateConfig { let release = false; + let custom_profile = None; + Ok(Self { out_dir, crate_dir, @@ -173,6 +176,7 @@ impl CrateConfig { executable, release, dioxus_config, + custom_profile, }) } @@ -186,6 +190,11 @@ impl CrateConfig { self } + pub fn set_profile(&mut self, profile: String) -> &mut Self { + self.custom_profile = Some(profile); + self + } + // pub fn with_build_options(&mut self, options: &BuildOptions) { // if let Some(name) = &options.example { // self.as_example(name.clone()); From 6aa609144d4b8dde995749ad2de4aaa8d69f16f4 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Mon, 6 Jun 2022 08:46:16 +0800 Subject: [PATCH 124/516] feat: add `profile` flag --- src/builder.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 31bb96f46..57738b1bc 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -51,7 +51,8 @@ pub fn build(config: &CrateConfig) -> Result<()> { if config.custom_profile.is_some() { let custom_profile = config.custom_profile.as_ref().unwrap(); - cmd.arg(format!("--profile {}", custom_profile)); + cmd.arg("--profile"); + cmd.arg(custom_profile); } match executable { @@ -194,6 +195,12 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { cmd.arg("--release"); } + if config.custom_profile.is_some() { + let custom_profile = config.custom_profile.as_ref().unwrap(); + cmd.arg("--profile"); + cmd.arg(custom_profile); + } + match &config.executable { crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), crate::ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), From d20e7ab50815014e67871ec2227413a085762690 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:18:22 +0800 Subject: [PATCH 125/516] feat: add `features` flag --- src/builder.rs | 12 ++++++++++++ src/cli/cfg.rs | 8 ++++++++ src/config.rs | 9 ++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 57738b1bc..46d4c15f5 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -55,6 +55,12 @@ pub fn build(config: &CrateConfig) -> Result<()> { 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), ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), @@ -201,6 +207,12 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { 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), crate::ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 11a1603c7..b8305e3a8 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -23,6 +23,10 @@ pub struct ConfigOptsBuild { /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long)] pub platform: Option, + + /// Space separated list of features to activate + #[clap(long)] + pub features: Option>, } #[derive(Clone, Debug, Default, Deserialize, Parser)] @@ -47,6 +51,10 @@ pub struct ConfigOptsServe { /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long)] pub platform: Option, + + /// Space separated list of features to activate + #[clap(long)] + pub features: Option>, } /// Ensure the given value for `--public-url` is formatted correctly. diff --git a/src/config.rs b/src/config.rs index 9679fadaa..6804ae6cd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -115,6 +115,7 @@ pub struct CrateConfig { pub dioxus_config: DioxusConfig, pub release: bool, pub custom_profile: Option, + pub features: Option>, } #[derive(Debug, Clone)] @@ -163,8 +164,8 @@ impl CrateConfig { let executable = ExecutableType::Binary(output_filename); let release = false; - let custom_profile = None; + let features = None; Ok(Self { out_dir, @@ -177,6 +178,7 @@ impl CrateConfig { release, dioxus_config, custom_profile, + features }) } @@ -195,6 +197,11 @@ impl CrateConfig { self } + pub fn set_features(&mut self, features: Vec) -> &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()); From 421d2ed2980d4e4d542f1b7b28c31e9ca279a563 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Mon, 6 Jun 2022 21:48:14 +0800 Subject: [PATCH 126/516] feat: add `verbose` flag --- src/builder.rs | 6 ++++++ src/cli/build/mod.rs | 1 + src/cli/cfg.rs | 10 ++++++++++ src/cli/serve/mod.rs | 1 + src/config.rs | 10 +++++++++- 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 46d4c15f5..02add84ad 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -48,6 +48,9 @@ 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(); @@ -200,6 +203,9 @@ 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(); diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 7f8c7b420..661f17a5a 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -14,6 +14,7 @@ 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()); diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index b8305e3a8..26409c370 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -12,6 +12,11 @@ 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, @@ -44,6 +49,11 @@ 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, diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 1b79284b7..d25aa5bad 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -19,6 +19,7 @@ impl Serve { // change the relase state. 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()); diff --git a/src/config.rs b/src/config.rs index 6804ae6cd..6e855002b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -114,6 +114,7 @@ pub struct CrateConfig { pub executable: ExecutableType, pub dioxus_config: DioxusConfig, pub release: bool, + pub verbose: bool, pub custom_profile: Option, pub features: Option>, } @@ -164,6 +165,7 @@ impl CrateConfig { let executable = ExecutableType::Binary(output_filename); let release = false; + let verbose = false; let custom_profile = None; let features = None; @@ -178,7 +180,8 @@ impl CrateConfig { release, dioxus_config, custom_profile, - features + features, + verbose, }) } @@ -192,6 +195,11 @@ 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 From 49b3ef83d33932bb8ee0e15f128f737994ef902a Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 09:37:26 -0500 Subject: [PATCH 127/516] create websocket to update rsx and report errors back --- Cargo.toml | 5 + src/hot_reload/mod.rs | 614 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 + src/server/mod.rs | 174 ++++++++++-- 4 files changed, 770 insertions(+), 26 deletions(-) create mode 100644 src/hot_reload/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 21e2dad7e..4acd41463 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,11 @@ flate2 = "1.0.22" tar = "0.4.38" tower = "0.4.12" +# hot reload +syn = "1.0" +dioxus-rsx-interpreter = { path = "../dioxus/packages/rsx_interpreter" } +proc-macro2 = { version = "1.0", features = ["span-locations"] } + [[bin]] path = "src/main.rs" name = "dioxus" diff --git a/src/hot_reload/mod.rs b/src/hot_reload/mod.rs new file mode 100644 index 000000000..d70cda29e --- /dev/null +++ b/src/hot_reload/mod.rs @@ -0,0 +1,614 @@ +use dioxus_rsx::{BodyNode, CallBody}; +use proc_macro2::TokenStream; +use syn::{File, Macro}; + +pub enum DiffResult { + CodeChanged, + RsxChanged(Vec<(Macro, TokenStream)>), +} + +pub fn find_rsx(new: &File, old: &File) -> DiffResult { + let mut rsx_calls = Vec::new(); + if new.items.len() != old.items.len() { + return DiffResult::CodeChanged; + } + for (new, old) in new.items.iter().zip(old.items.iter()) { + if find_rsx_item(new, old, &mut rsx_calls) { + return DiffResult::CodeChanged; + } + } + DiffResult::RsxChanged(rsx_calls) +} + +fn find_rsx_item( + new: &syn::Item, + old: &syn::Item, + rsx_calls: &mut Vec<(Macro, TokenStream)>, +) -> bool { + match (new, old) { + (syn::Item::Const(new_item), syn::Item::Const(old_item)) => { + find_rsx_expr(&new_item.expr, &old_item.expr, rsx_calls) + || new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.const_token != old_item.const_token + || new_item.ident != old_item.ident + || new_item.colon_token != old_item.colon_token + || new_item.ty != old_item.ty + || new_item.eq_token != old_item.eq_token + || new_item.semi_token != old_item.semi_token + } + (syn::Item::Enum(new_item), syn::Item::Enum(old_item)) => { + if new_item.variants.len() != old_item.variants.len() { + return true; + } + for (new_varient, old_varient) in new_item.variants.iter().zip(old_item.variants.iter()) + { + match (&new_varient.discriminant, &old_varient.discriminant) { + (Some((new_eq, new_expr)), Some((old_eq, old_expr))) => { + if find_rsx_expr(new_expr, old_expr, rsx_calls) || new_eq != old_eq { + return true; + } + } + (None, None) => (), + _ => return true, + } + if new_varient.attrs != old_varient.attrs + || new_varient.ident != old_varient.ident + || new_varient.fields != old_varient.fields + { + return true; + } + } + new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.enum_token != old_item.enum_token + || new_item.ident != old_item.ident + || new_item.generics != old_item.generics + || new_item.brace_token != old_item.brace_token + } + (syn::Item::ExternCrate(new_item), syn::Item::ExternCrate(old_item)) => { + old_item != new_item + } + (syn::Item::Fn(new_item), syn::Item::Fn(old_item)) => { + find_rsx_block(&new_item.block, &old_item.block, rsx_calls) + || new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.sig != old_item.sig + } + (syn::Item::ForeignMod(new_item), syn::Item::ForeignMod(old_item)) => old_item != new_item, + (syn::Item::Impl(new_item), syn::Item::Impl(old_item)) => { + if new_item.items.len() != old_item.items.len() { + return true; + } + for (new_item, old_item) in new_item.items.iter().zip(old_item.items.iter()) { + if match (new_item, old_item) { + (syn::ImplItem::Const(new_item), syn::ImplItem::Const(old_item)) => { + find_rsx_expr(&new_item.expr, &old_item.expr, rsx_calls) + } + (syn::ImplItem::Method(new_item), syn::ImplItem::Method(old_item)) => { + find_rsx_block(&new_item.block, &old_item.block, rsx_calls) + } + (syn::ImplItem::Type(new_item), syn::ImplItem::Type(old_item)) => { + old_item != new_item + } + (syn::ImplItem::Macro(new_item), syn::ImplItem::Macro(old_item)) => { + old_item != new_item + } + _ => true, + } { + return true; + } + } + new_item.attrs != old_item.attrs + || new_item.defaultness != old_item.defaultness + || new_item.unsafety != old_item.unsafety + || new_item.impl_token != old_item.impl_token + || new_item.generics != old_item.generics + || new_item.trait_ != old_item.trait_ + || new_item.self_ty != old_item.self_ty + || new_item.brace_token != old_item.brace_token + } + (syn::Item::Macro(new_item), syn::Item::Macro(old_item)) => old_item != new_item, + (syn::Item::Macro2(new_item), syn::Item::Macro2(old_item)) => old_item != new_item, + (syn::Item::Mod(new_item), syn::Item::Mod(old_item)) => { + match (&new_item.content, &old_item.content) { + (Some((_, new_items)), Some((_, old_items))) => { + if new_items.len() != old_items.len() { + return true; + } + for (new_item, old_item) in new_items.iter().zip(old_items.iter()) { + if find_rsx_item(new_item, old_item, rsx_calls) { + return true; + } + } + new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.mod_token != old_item.mod_token + || new_item.ident != old_item.ident + || new_item.semi != old_item.semi + } + (None, None) => { + new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.mod_token != old_item.mod_token + || new_item.ident != old_item.ident + || new_item.semi != old_item.semi + } + _ => true, + } + } + (syn::Item::Static(new_item), syn::Item::Static(old_item)) => { + find_rsx_expr(&new_item.expr, &old_item.expr, rsx_calls) + || new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.static_token != old_item.static_token + || new_item.mutability != old_item.mutability + || new_item.ident != old_item.ident + || new_item.colon_token != old_item.colon_token + || new_item.ty != old_item.ty + || new_item.eq_token != old_item.eq_token + || new_item.semi_token != old_item.semi_token + } + (syn::Item::Struct(new_item), syn::Item::Struct(old_item)) => old_item != new_item, + (syn::Item::Trait(new_item), syn::Item::Trait(old_item)) => { + find_rsx_trait(new_item, old_item, rsx_calls) + } + (syn::Item::TraitAlias(new_item), syn::Item::TraitAlias(old_item)) => old_item != new_item, + (syn::Item::Type(new_item), syn::Item::Type(old_item)) => old_item != new_item, + (syn::Item::Union(new_item), syn::Item::Union(old_item)) => old_item != new_item, + (syn::Item::Use(new_item), syn::Item::Use(old_item)) => old_item != new_item, + (syn::Item::Verbatim(_), syn::Item::Verbatim(_)) => false, + _ => return true, + } +} + +fn find_rsx_trait( + new_item: &syn::ItemTrait, + old_item: &syn::ItemTrait, + rsx_calls: &mut Vec<(Macro, TokenStream)>, +) -> bool { + if new_item.items.len() != old_item.items.len() { + return true; + } + for (new_item, old_item) in new_item.items.iter().zip(old_item.items.iter()) { + if match (new_item, old_item) { + (syn::TraitItem::Const(new_item), syn::TraitItem::Const(old_item)) => { + if let (Some((_, new_expr)), Some((_, old_expr))) = + (&new_item.default, &old_item.default) + { + find_rsx_expr(new_expr, old_expr, rsx_calls) + } else { + true + } + } + (syn::TraitItem::Method(new_item), syn::TraitItem::Method(old_item)) => { + if let (Some(new_block), Some(old_block)) = (&new_item.default, &old_item.default) { + find_rsx_block(new_block, old_block, rsx_calls) + } else { + true + } + } + (syn::TraitItem::Type(new_item), syn::TraitItem::Type(old_item)) => { + old_item != new_item + } + (syn::TraitItem::Macro(new_item), syn::TraitItem::Macro(old_item)) => { + old_item != new_item + } + _ => true, + } { + return true; + } + } + new_item.attrs != old_item.attrs + || new_item.vis != old_item.vis + || new_item.unsafety != old_item.unsafety + || new_item.auto_token != old_item.auto_token + || new_item.ident != old_item.ident + || new_item.generics != old_item.generics + || new_item.colon_token != old_item.colon_token + || new_item.supertraits != old_item.supertraits + || new_item.brace_token != old_item.brace_token +} + +fn find_rsx_block( + new_block: &syn::Block, + old_block: &syn::Block, + rsx_calls: &mut Vec<(Macro, TokenStream)>, +) -> bool { + if new_block.stmts.len() != old_block.stmts.len() { + return true; + } + for (new_stmt, old_stmt) in new_block.stmts.iter().zip(old_block.stmts.iter()) { + if find_rsx_stmt(new_stmt, old_stmt, rsx_calls) { + return true; + } + } + new_block.brace_token != old_block.brace_token +} + +fn find_rsx_stmt( + new_stmt: &syn::Stmt, + old_stmt: &syn::Stmt, + rsx_calls: &mut Vec<(Macro, TokenStream)>, +) -> bool { + match (new_stmt, old_stmt) { + (syn::Stmt::Local(new_local), syn::Stmt::Local(old_local)) => { + (match (&new_local.init, &old_local.init) { + (Some((new_eq, new_expr)), Some((old_eq, old_expr))) => { + find_rsx_expr(new_expr, old_expr, rsx_calls) || new_eq != old_eq + } + (None, None) => false, + _ => true, + } || new_local.attrs != old_local.attrs + || new_local.let_token != old_local.let_token + || new_local.pat != old_local.pat + || new_local.semi_token != old_local.semi_token) + } + (syn::Stmt::Item(new_item), syn::Stmt::Item(old_item)) => { + find_rsx_item(new_item, old_item, rsx_calls) + } + (syn::Stmt::Expr(new_expr), syn::Stmt::Expr(old_expr)) => { + find_rsx_expr(new_expr, old_expr, rsx_calls) + } + (syn::Stmt::Semi(new_expr, new_semi), syn::Stmt::Semi(old_expr, old_semi)) => { + find_rsx_expr(new_expr, old_expr, rsx_calls) || new_semi != old_semi + } + _ => true, + } +} + +fn find_rsx_expr( + new_expr: &syn::Expr, + old_expr: &syn::Expr, + rsx_calls: &mut Vec<(Macro, TokenStream)>, +) -> bool { + match (new_expr, old_expr) { + (syn::Expr::Array(new_expr), syn::Expr::Array(old_expr)) => { + if new_expr.elems.len() != old_expr.elems.len() { + return true; + } + for (new_el, old_el) in new_expr.elems.iter().zip(old_expr.elems.iter()) { + if find_rsx_expr(new_el, old_el, rsx_calls) { + return true; + } + } + new_expr.attrs != old_expr.attrs || new_expr.bracket_token != old_expr.bracket_token + } + (syn::Expr::Assign(new_expr), syn::Expr::Assign(old_expr)) => { + find_rsx_expr(&new_expr.left, &old_expr.left, rsx_calls) + || find_rsx_expr(&new_expr.right, &old_expr.right, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.eq_token != old_expr.eq_token + } + (syn::Expr::AssignOp(new_expr), syn::Expr::AssignOp(old_expr)) => { + find_rsx_expr(&new_expr.left, &old_expr.left, rsx_calls) + || find_rsx_expr(&new_expr.right, &old_expr.right, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.op != old_expr.op + } + (syn::Expr::Async(new_expr), syn::Expr::Async(old_expr)) => { + find_rsx_block(&new_expr.block, &old_expr.block, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.async_token != old_expr.async_token + || new_expr.capture != old_expr.capture + } + (syn::Expr::Await(new_expr), syn::Expr::Await(old_expr)) => { + find_rsx_expr(&new_expr.base, &old_expr.base, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.dot_token != old_expr.dot_token + || new_expr.await_token != old_expr.await_token + } + (syn::Expr::Binary(new_expr), syn::Expr::Binary(old_expr)) => { + find_rsx_expr(&new_expr.left, &old_expr.left, rsx_calls) + || find_rsx_expr(&new_expr.right, &old_expr.right, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.op != old_expr.op + } + (syn::Expr::Block(new_expr), syn::Expr::Block(old_expr)) => { + find_rsx_block(&new_expr.block, &old_expr.block, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.label != old_expr.label + } + (syn::Expr::Box(new_expr), syn::Expr::Box(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.box_token != old_expr.box_token + } + (syn::Expr::Break(new_expr), syn::Expr::Break(old_expr)) => { + match (&new_expr.expr, &old_expr.expr) { + (Some(new_inner), Some(old_inner)) => { + find_rsx_expr(new_inner, old_inner, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.break_token != old_expr.break_token + || new_expr.label != old_expr.label + } + (None, None) => { + new_expr.attrs != old_expr.attrs + || new_expr.break_token != old_expr.break_token + || new_expr.label != old_expr.label + } + _ => true, + } + } + (syn::Expr::Call(new_expr), syn::Expr::Call(old_expr)) => { + find_rsx_expr(&new_expr.func, &old_expr.func, rsx_calls); + if new_expr.args.len() != old_expr.args.len() { + return true; + } + for (new_arg, old_arg) in new_expr.args.iter().zip(old_expr.args.iter()) { + if find_rsx_expr(new_arg, old_arg, rsx_calls) { + return true; + } + } + new_expr.attrs != old_expr.attrs || new_expr.paren_token != old_expr.paren_token + } + (syn::Expr::Cast(new_expr), syn::Expr::Cast(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.as_token != old_expr.as_token + || new_expr.ty != old_expr.ty + } + (syn::Expr::Closure(new_expr), syn::Expr::Closure(old_expr)) => { + find_rsx_expr(&new_expr.body, &old_expr.body, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.movability != old_expr.movability + || new_expr.asyncness != old_expr.asyncness + || new_expr.capture != old_expr.capture + || new_expr.or1_token != old_expr.or1_token + || new_expr.inputs != old_expr.inputs + || new_expr.or2_token != old_expr.or2_token + || new_expr.output != old_expr.output + } + (syn::Expr::Continue(new_expr), syn::Expr::Continue(old_expr)) => old_expr != new_expr, + (syn::Expr::Field(new_expr), syn::Expr::Field(old_expr)) => { + find_rsx_expr(&new_expr.base, &old_expr.base, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.dot_token != old_expr.dot_token + || new_expr.member != old_expr.member + } + (syn::Expr::ForLoop(new_expr), syn::Expr::ForLoop(old_expr)) => { + find_rsx_block(&new_expr.body, &old_expr.body, rsx_calls) + || find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.label != old_expr.label + || new_expr.for_token != old_expr.for_token + || new_expr.pat != old_expr.pat + || new_expr.in_token != old_expr.in_token + } + (syn::Expr::Group(new_expr), syn::Expr::Group(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + } + (syn::Expr::If(new_expr), syn::Expr::If(old_expr)) => { + if find_rsx_expr(&new_expr.cond, &old_expr.cond, rsx_calls) + || find_rsx_block(&new_expr.then_branch, &old_expr.then_branch, rsx_calls) + { + return true; + } + match (&new_expr.else_branch, &old_expr.else_branch) { + (Some((new_tok, new_else)), Some((old_tok, old_else))) => { + find_rsx_expr(new_else, old_else, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.if_token != old_expr.if_token + || new_expr.cond != old_expr.cond + || new_tok != old_tok + } + (None, None) => { + new_expr.attrs != old_expr.attrs + || new_expr.if_token != old_expr.if_token + || new_expr.cond != old_expr.cond + } + _ => true, + } + } + (syn::Expr::Index(new_expr), syn::Expr::Index(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || find_rsx_expr(&new_expr.index, &old_expr.index, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.bracket_token != old_expr.bracket_token + } + (syn::Expr::Let(new_expr), syn::Expr::Let(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.let_token != old_expr.let_token + || new_expr.pat != old_expr.pat + || new_expr.eq_token != old_expr.eq_token + } + (syn::Expr::Lit(new_expr), syn::Expr::Lit(old_expr)) => old_expr != new_expr, + (syn::Expr::Loop(new_expr), syn::Expr::Loop(old_expr)) => { + find_rsx_block(&new_expr.body, &old_expr.body, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.label != old_expr.label + || new_expr.loop_token != old_expr.loop_token + } + (syn::Expr::Macro(new_expr), syn::Expr::Macro(old_expr)) => { + let new_mac = &new_expr.mac; + let old_mac = &old_expr.mac; + if new_mac.path.get_ident().map(|ident| ident.to_string()) == Some("rsx".to_string()) + && old_mac.path.get_ident().map(|ident| ident.to_string()) + == Some("rsx".to_string()) + { + rsx_calls.push((old_mac.clone(), new_mac.tokens.clone())); + false + } else { + new_expr != old_expr + } + } + (syn::Expr::Match(new_expr), syn::Expr::Match(old_expr)) => { + if find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) { + return true; + } + for (new_arm, old_arm) in new_expr.arms.iter().zip(old_expr.arms.iter()) { + match (&new_arm.guard, &old_arm.guard) { + (Some((new_tok, new_expr)), Some((old_tok, old_expr))) => { + if find_rsx_expr(new_expr, old_expr, rsx_calls) || new_tok != old_tok { + return true; + } + } + (None, None) => (), + _ => return true, + } + if find_rsx_expr(&new_arm.body, &old_arm.body, rsx_calls) + || new_arm.attrs != old_arm.attrs + || new_arm.pat != old_arm.pat + || new_arm.fat_arrow_token != old_arm.fat_arrow_token + || new_arm.comma != old_arm.comma + { + return true; + } + } + new_expr.attrs != old_expr.attrs + || new_expr.match_token != old_expr.match_token + || new_expr.brace_token != old_expr.brace_token + } + (syn::Expr::MethodCall(new_expr), syn::Expr::MethodCall(old_expr)) => { + if find_rsx_expr(&new_expr.receiver, &old_expr.receiver, rsx_calls) { + return true; + } + for (new_arg, old_arg) in new_expr.args.iter().zip(old_expr.args.iter()) { + if find_rsx_expr(new_arg, old_arg, rsx_calls) { + return true; + } + } + new_expr.attrs != old_expr.attrs + || new_expr.dot_token != old_expr.dot_token + || new_expr.method != old_expr.method + || new_expr.turbofish != old_expr.turbofish + || new_expr.paren_token != old_expr.paren_token + } + (syn::Expr::Paren(new_expr), syn::Expr::Paren(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.paren_token != old_expr.paren_token + } + (syn::Expr::Path(new_expr), syn::Expr::Path(old_expr)) => old_expr != new_expr, + (syn::Expr::Range(new_expr), syn::Expr::Range(old_expr)) => { + match (&new_expr.from, &old_expr.from) { + (Some(new_expr), Some(old_expr)) => { + if find_rsx_expr(new_expr, old_expr, rsx_calls) { + return true; + } + } + (None, None) => (), + _ => return true, + } + match (&new_expr.to, &old_expr.to) { + (Some(new_inner), Some(old_inner)) => { + find_rsx_expr(new_inner, old_inner, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.limits != old_expr.limits + } + (None, None) => { + new_expr.attrs != old_expr.attrs || new_expr.limits != old_expr.limits + } + _ => true, + } + } + (syn::Expr::Reference(new_expr), syn::Expr::Reference(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.and_token != old_expr.and_token + || new_expr.mutability != old_expr.mutability + } + (syn::Expr::Repeat(new_expr), syn::Expr::Repeat(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || find_rsx_expr(&new_expr.len, &old_expr.len, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.bracket_token != old_expr.bracket_token + || new_expr.semi_token != old_expr.semi_token + } + (syn::Expr::Return(new_expr), syn::Expr::Return(old_expr)) => { + match (&new_expr.expr, &old_expr.expr) { + (Some(new_inner), Some(old_inner)) => { + find_rsx_expr(new_inner, old_inner, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.return_token != old_expr.return_token + } + (None, None) => { + new_expr.attrs != old_expr.attrs + || new_expr.return_token != old_expr.return_token + } + _ => true, + } + } + (syn::Expr::Struct(new_expr), syn::Expr::Struct(old_expr)) => { + match (&new_expr.rest, &old_expr.rest) { + (Some(new_expr), Some(old_expr)) => { + if find_rsx_expr(new_expr, old_expr, rsx_calls) { + return true; + } + } + (None, None) => (), + _ => return true, + } + for (new_field, old_field) in new_expr.fields.iter().zip(old_expr.fields.iter()) { + if find_rsx_expr(&new_field.expr, &old_field.expr, rsx_calls) + || new_field.attrs != old_field.attrs + || new_field.member != old_field.member + || new_field.colon_token != old_field.colon_token + { + return true; + } + } + new_expr.attrs != old_expr.attrs + || new_expr.path != old_expr.path + || new_expr.brace_token != old_expr.brace_token + || new_expr.dot2_token != old_expr.dot2_token + } + (syn::Expr::Try(new_expr), syn::Expr::Try(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.question_token != old_expr.question_token + } + (syn::Expr::TryBlock(new_expr), syn::Expr::TryBlock(old_expr)) => { + find_rsx_block(&new_expr.block, &old_expr.block, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.try_token != old_expr.try_token + } + (syn::Expr::Tuple(new_expr), syn::Expr::Tuple(old_expr)) => { + for (new_el, old_el) in new_expr.elems.iter().zip(old_expr.elems.iter()) { + if find_rsx_expr(new_el, old_el, rsx_calls) { + return true; + } + } + new_expr.attrs != old_expr.attrs || new_expr.paren_token != old_expr.paren_token + } + (syn::Expr::Type(new_expr), syn::Expr::Type(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.colon_token != old_expr.colon_token + || new_expr.ty != old_expr.ty + } + (syn::Expr::Unary(new_expr), syn::Expr::Unary(old_expr)) => { + find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.op != old_expr.op + } + (syn::Expr::Unsafe(new_expr), syn::Expr::Unsafe(old_expr)) => { + find_rsx_block(&new_expr.block, &old_expr.block, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.unsafe_token != old_expr.unsafe_token + } + (syn::Expr::While(new_expr), syn::Expr::While(old_expr)) => { + find_rsx_expr(&new_expr.cond, &old_expr.cond, rsx_calls) + || find_rsx_block(&new_expr.body, &old_expr.body, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.label != old_expr.label + || new_expr.while_token != old_expr.while_token + } + (syn::Expr::Yield(new_expr), syn::Expr::Yield(old_expr)) => { + match (&new_expr.expr, &old_expr.expr) { + (Some(new_inner), Some(old_inner)) => { + find_rsx_expr(new_inner, old_inner, rsx_calls) + || new_expr.attrs != old_expr.attrs + || new_expr.yield_token != old_expr.yield_token + } + (None, None) => { + new_expr.attrs != old_expr.attrs || new_expr.yield_token != old_expr.yield_token + } + _ => true, + } + } + (syn::Expr::Verbatim(_), syn::Expr::Verbatim(_)) => false, + _ => true, + } +} diff --git a/src/lib.rs b/src/lib.rs index 8cd4b1f2d..722b03a06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,3 +18,6 @@ pub use error::*; pub mod logging; pub use logging::*; + +pub mod hot_reload; +pub use hot_reload::*; diff --git a/src/server/mod.rs b/src/server/mod.rs index 1df13ca6a..230136e54 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,18 +7,28 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; +use std::{fs::File, io::Read}; +use syn::__private::ToTokens; use std::{path::PathBuf, sync::Arc}; use tower::ServiceBuilder; use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; -use crate::{builder, serve::Serve, CrateConfig, Result}; +use crate::{builder, find_rsx, serve::Serve, CrateConfig, Result}; use tokio::sync::broadcast; -struct WsRelodState { +use std::collections::HashMap; + +use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; + +struct WsReloadState { update: broadcast::Sender, } +struct HotReloadState { + messages: broadcast::Sender, +} + pub async fn startup(config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); @@ -26,35 +36,20 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let (reload_tx, _) = broadcast::channel(100); - let ws_reload_state = Arc::new(WsRelodState { + let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), }); + let hot_reload_tx = broadcast::channel(100).0; + let hot_reload_state = Arc::new(HotReloadState { + messages: hot_reload_tx.clone(), + }); + let mut last_update_time = chrono::Local::now().timestamp(); // file watcher: check file change let watcher_conf = config.clone(); - let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { - if chrono::Local::now().timestamp() > last_update_time { - log::info!("Start to rebuild project..."); - if builder::build(&watcher_conf).is_ok() { - // change the websocket reload state to true; - // the page will auto-reload. - if watcher_conf - .dioxus_config - .web - .watcher - .reload_html - .unwrap_or(false) - { - let _ = Serve::regen_dev_page(&watcher_conf); - } - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); - } - } - }) - .unwrap(); + let mut old_files_parsed: HashMap = HashMap::new(); let allow_watch_path = config .dioxus_config .web @@ -63,6 +58,92 @@ pub async fn startup(config: CrateConfig) -> Result<()> { .clone() .unwrap_or_else(|| vec![PathBuf::from("src")]); + let crate_dir = config.crate_dir.clone(); + + let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { + if let Ok(evt) = evt { + if let notify::EventKind::Modify(_) = evt.kind { + for path in evt.paths { + log::info!("File changed: {}", path.display()); + let mut file = File::open(path.clone()).unwrap(); + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + if src.is_empty() { + continue; + } + if let Ok(syntax) = syn::parse_file(&src) { + if let Some(old_str) = old_files_parsed.get(path.to_str().unwrap()) { + if let Ok(old) = syn::parse_file(&old_str) { + match find_rsx(&syntax, &old) { + crate::DiffResult::CodeChanged => { + log::info!("reload required"); + if chrono::Local::now().timestamp() > last_update_time { + log::info!("Start to rebuild project..."); + if builder::build(&watcher_conf).is_ok() { + // change the websocket reload state to true; + // the page will auto-reload. + if watcher_conf + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&watcher_conf); + } + let _ = reload_tx.send("reload".into()); + old_files_parsed.insert( + path.to_str().unwrap().to_string(), + src, + ); + last_update_time = chrono::Local::now().timestamp(); + } + } + } + crate::DiffResult::RsxChanged(changed) => { + for (old, new) in changed.into_iter() { + if let Some(hr) = old + .to_token_stream() + .into_iter() + .map(|tree| { + let location = tree.span(); + let start = location.start(); + CodeLocation { + file: path + .strip_prefix(&crate_dir) + .unwrap() + .display() + .to_string(), + line: start.line as u32, + column: start.column as u32 + 1, + } + }) + .min_by(|cl1, cl2| { + cl1.line + .cmp(&cl2.line) + .then(cl1.column.cmp(&cl2.column)) + }) + { + let rsx = new.to_string(); + let _ = hot_reload_tx.send(SetRsxMessage { + location: hr, + new_text: rsx, + }); + } + } + } + } + } + } else { + old_files_parsed.insert(path.to_str().unwrap().to_string(), src); + } + } + } + } + } + }) + .unwrap(); + for sub_path in allow_watch_path { watcher .watch( @@ -115,6 +196,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let router = Router::new() .route("/_dioxus/ws", get(ws_handler)) + .route("/_dioxus/hot_reload", get(hot_reload_handler)) .fallback( get_service(file_service).handle_error(|error: std::io::Error| async move { ( @@ -125,7 +207,12 @@ pub async fn startup(config: CrateConfig) -> Result<()> { ); axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) - .serve(router.layer(Extension(ws_reload_state)).into_make_service()) + .serve( + router + .layer(Extension(ws_reload_state)) + .layer(Extension(hot_reload_state)) + .into_make_service(), + ) .await?; Ok(()) @@ -134,7 +221,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { async fn ws_handler( ws: WebSocketUpgrade, _: Option>, - Extension(state): Extension>, + Extension(state): Extension>, ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { let mut rx = state.update.subscribe(); @@ -157,3 +244,38 @@ async fn ws_handler( reload_watcher.await.unwrap(); }) } + +async fn hot_reload_handler( + ws: WebSocketUpgrade, + _: Option>, + Extension(state): Extension>, +) -> impl IntoResponse { + ws.on_upgrade(|mut socket| async move { + println!("🔥 Hot Reload WebSocket is connected."); + let mut rx = state.messages.subscribe(); + loop { + let read_set_rsx = rx.recv(); + let read_err = socket.recv(); + tokio::select! { + err = read_err => { + if let Some(Ok(Message::Text(err))) = err { + let error: RecompileReason = serde_json::from_str(&err).unwrap(); + log::error!("{:?}", error); + }; + }, + set_rsx = read_set_rsx => { + if let Ok(rsx) = set_rsx{ + if socket + .send(Message::Text(serde_json::to_string(&rsx).unwrap())) + .await + .is_err() + { + break; + }; + // println!("{:?}", rsx); + } + } + }; + } + }) +} From 3ccd65b582b8320b24c8921829b777084a6d2e1e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 10:35:18 -0500 Subject: [PATCH 128/516] remove unused import --- src/hot_reload/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hot_reload/mod.rs b/src/hot_reload/mod.rs index d70cda29e..3aced9567 100644 --- a/src/hot_reload/mod.rs +++ b/src/hot_reload/mod.rs @@ -1,4 +1,3 @@ -use dioxus_rsx::{BodyNode, CallBody}; use proc_macro2::TokenStream; use syn::{File, Macro}; From 61e9f56816495ad18c367cc72038409e60382ddc Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 11:37:48 -0500 Subject: [PATCH 129/516] automaticly reload when rsx requires it --- src/server/mod.rs | 59 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 230136e54..c4ab60385 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,7 +7,7 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; -use std::{fs::File, io::Read}; +use std::{fs::File, io::Read, sync::Mutex}; use syn::__private::ToTokens; use std::{path::PathBuf, sync::Arc}; @@ -23,10 +23,13 @@ use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage struct WsReloadState { update: broadcast::Sender, + last_file_rebuild: Arc>>, + watcher_config: CrateConfig, } struct HotReloadState { messages: broadcast::Sender, + update: broadcast::Sender, } pub async fn startup(config: CrateConfig) -> Result<()> { @@ -36,20 +39,23 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let (reload_tx, _) = broadcast::channel(100); + let last_file_rebuild = Arc::new(Mutex::new(HashMap::new())); + let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), + last_file_rebuild: last_file_rebuild.clone(), + watcher_config: config.clone(), }); let hot_reload_tx = broadcast::channel(100).0; let hot_reload_state = Arc::new(HotReloadState { messages: hot_reload_tx.clone(), + update: reload_tx.clone(), }); let mut last_update_time = chrono::Local::now().timestamp(); // file watcher: check file change - let watcher_conf = config.clone(); - let mut old_files_parsed: HashMap = HashMap::new(); let allow_watch_path = config .dioxus_config .web @@ -64,7 +70,6 @@ pub async fn startup(config: CrateConfig) -> Result<()> { if let Ok(evt) = evt { if let notify::EventKind::Modify(_) = evt.kind { for path in evt.paths { - log::info!("File changed: {}", path.display()); let mut file = File::open(path.clone()).unwrap(); let mut src = String::new(); file.read_to_string(&mut src).expect("Unable to read file"); @@ -72,35 +77,19 @@ pub async fn startup(config: CrateConfig) -> Result<()> { continue; } if let Ok(syntax) = syn::parse_file(&src) { - if let Some(old_str) = old_files_parsed.get(path.to_str().unwrap()) { + let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); + if let Some(old_str) = last_file_rebuild.get(path.to_str().unwrap()) { if let Ok(old) = syn::parse_file(&old_str) { match find_rsx(&syntax, &old) { crate::DiffResult::CodeChanged => { log::info!("reload required"); if chrono::Local::now().timestamp() > last_update_time { - log::info!("Start to rebuild project..."); - if builder::build(&watcher_conf).is_ok() { - // change the websocket reload state to true; - // the page will auto-reload. - if watcher_conf - .dioxus_config - .web - .watcher - .reload_html - .unwrap_or(false) - { - let _ = Serve::regen_dev_page(&watcher_conf); - } - let _ = reload_tx.send("reload".into()); - old_files_parsed.insert( - path.to_str().unwrap().to_string(), - src, - ); - last_update_time = chrono::Local::now().timestamp(); - } + let _ = reload_tx.send("reload".into()); + last_update_time = chrono::Local::now().timestamp(); } } crate::DiffResult::RsxChanged(changed) => { + log::info!("reloading rsx"); for (old, new) in changed.into_iter() { if let Some(hr) = old .to_token_stream() @@ -135,7 +124,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { } } } else { - old_files_parsed.insert(path.to_str().unwrap().to_string(), src); + last_file_rebuild.insert(path.to_str().unwrap().to_string(), src); } } } @@ -229,6 +218,22 @@ async fn ws_handler( loop { let v = rx.recv().await.unwrap(); if v == "reload" { + log::info!("Start to rebuild project..."); + if builder::build(&state.watcher_config).is_ok() { + // change the websocket reload state to true; + // the page will auto-reload. + if state + .watcher_config + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&state.watcher_config); + } + *state.last_file_rebuild.lock().unwrap() = HashMap::new(); + } // ignore the error if socket .send(Message::Text(String::from("reload"))) @@ -261,6 +266,7 @@ async fn hot_reload_handler( if let Some(Ok(Message::Text(err))) = err { let error: RecompileReason = serde_json::from_str(&err).unwrap(); log::error!("{:?}", error); + state.update.send("reload".to_string()).unwrap(); }; }, set_rsx = read_set_rsx => { @@ -272,7 +278,6 @@ async fn hot_reload_handler( { break; }; - // println!("{:?}", rsx); } } }; From f19958d766c5584c89df6d617170e87f275591bd Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 11:58:09 -0500 Subject: [PATCH 130/516] move into feature --- Cargo.toml | 10 +-- src/lib.rs | 2 +- src/server/mod.rs | 159 +++++++++++++++++++++++++++------------------- 3 files changed, 100 insertions(+), 71 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4acd41463..770fd7b1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,10 +46,12 @@ flate2 = "1.0.22" tar = "0.4.38" tower = "0.4.12" -# hot reload -syn = "1.0" -dioxus-rsx-interpreter = { path = "../dioxus/packages/rsx_interpreter" } -proc-macro2 = { version = "1.0", features = ["span-locations"] } +syn = { version = "1.0", optional = true } +dioxus-rsx-interpreter = { path = "../dioxus/packages/rsx_interpreter", optional = true } +proc-macro2 = { version = "1.0", features = ["span-locations"], optional = true } + +[features] +hot_reload = ["dioxus-rsx-interpreter", "proc-macro2", "syn"] [[bin]] path = "src/main.rs" diff --git a/src/lib.rs b/src/lib.rs index 722b03a06..683b7251a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,5 +19,5 @@ pub use error::*; pub mod logging; pub use logging::*; +#[cfg(feature = "hot_reload")] pub mod hot_reload; -pub use hot_reload::*; diff --git a/src/server/mod.rs b/src/server/mod.rs index c4ab60385..18d50fc57 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,26 +7,34 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; -use std::{fs::File, io::Read, sync::Mutex}; -use syn::__private::ToTokens; +use std::{fs::File, io::Read}; use std::{path::PathBuf, sync::Arc}; use tower::ServiceBuilder; use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; -use crate::{builder, find_rsx, serve::Serve, CrateConfig, Result}; +use crate::{builder, serve::Serve, CrateConfig, Result}; use tokio::sync::broadcast; -use std::collections::HashMap; - -use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; +#[cfg(feature = "hot_reload")] +mod hot_reload_improts { + pub use crate::hot_reload::{find_rsx, DiffResult}; + pub use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; + pub use std::collections::HashMap; + pub use std::sync::Mutex; + pub use syn::__private::ToTokens; +} +#[cfg(feature = "hot_reload")] +use hot_reload_improts::*; struct WsReloadState { update: broadcast::Sender, + #[cfg(feature = "hot_reload")] last_file_rebuild: Arc>>, watcher_config: CrateConfig, } +#[cfg(feature = "hot_reload")] struct HotReloadState { messages: broadcast::Sender, update: broadcast::Sender, @@ -39,19 +47,24 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let (reload_tx, _) = broadcast::channel(100); + #[cfg(feature = "hot_reload")] let last_file_rebuild = Arc::new(Mutex::new(HashMap::new())); - - let ws_reload_state = Arc::new(WsReloadState { - update: reload_tx.clone(), - last_file_rebuild: last_file_rebuild.clone(), - watcher_config: config.clone(), - }); - + #[cfg(feature = "hot_reload")] let hot_reload_tx = broadcast::channel(100).0; + #[cfg(feature = "hot_reload")] let hot_reload_state = Arc::new(HotReloadState { messages: hot_reload_tx.clone(), update: reload_tx.clone(), }); + #[cfg(feature = "hot_reload")] + let crate_dir = config.crate_dir.clone(); + + let ws_reload_state = Arc::new(WsReloadState { + update: reload_tx.clone(), + #[cfg(feature = "hot_reload")] + last_file_rebuild: last_file_rebuild.clone(), + watcher_config: config.clone(), + }); let mut last_update_time = chrono::Local::now().timestamp(); @@ -64,8 +77,6 @@ pub async fn startup(config: CrateConfig) -> Result<()> { .clone() .unwrap_or_else(|| vec![PathBuf::from("src")]); - let crate_dir = config.crate_dir.clone(); - let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { if let Ok(evt) = evt { if let notify::EventKind::Modify(_) = evt.kind { @@ -76,55 +87,66 @@ pub async fn startup(config: CrateConfig) -> Result<()> { if src.is_empty() { continue; } - if let Ok(syntax) = syn::parse_file(&src) { - let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); - if let Some(old_str) = last_file_rebuild.get(path.to_str().unwrap()) { - if let Ok(old) = syn::parse_file(&old_str) { - match find_rsx(&syntax, &old) { - crate::DiffResult::CodeChanged => { - log::info!("reload required"); - if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); + #[cfg(feature = "hot_reload")] + { + if let Ok(syntax) = syn::parse_file(&src) { + let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); + if let Some(old_str) = last_file_rebuild.get(path.to_str().unwrap()) { + if let Ok(old) = syn::parse_file(&old_str) { + match find_rsx(&syntax, &old) { + DiffResult::CodeChanged => { + log::info!("reload required"); + if chrono::Local::now().timestamp() > last_update_time { + let _ = reload_tx.send("reload".into()); + last_update_time = chrono::Local::now().timestamp(); + } } - } - crate::DiffResult::RsxChanged(changed) => { - log::info!("reloading rsx"); - for (old, new) in changed.into_iter() { - if let Some(hr) = old - .to_token_stream() - .into_iter() - .map(|tree| { - let location = tree.span(); - let start = location.start(); - CodeLocation { - file: path - .strip_prefix(&crate_dir) - .unwrap() - .display() - .to_string(), - line: start.line as u32, - column: start.column as u32 + 1, - } - }) - .min_by(|cl1, cl2| { - cl1.line - .cmp(&cl2.line) - .then(cl1.column.cmp(&cl2.column)) - }) - { - let rsx = new.to_string(); - let _ = hot_reload_tx.send(SetRsxMessage { - location: hr, - new_text: rsx, - }); + DiffResult::RsxChanged(changed) => { + log::info!("reloading rsx"); + for (old, new) in changed.into_iter() { + if let Some(hr) = old + .to_token_stream() + .into_iter() + .map(|tree| { + let location = tree.span(); + let start = location.start(); + CodeLocation { + file: path + .strip_prefix(&crate_dir) + .unwrap() + .display() + .to_string(), + line: start.line as u32, + column: start.column as u32 + 1, + } + }) + .min_by(|cl1, cl2| { + cl1.line + .cmp(&cl2.line) + .then(cl1.column.cmp(&cl2.column)) + }) + { + let rsx = new.to_string(); + let _ = hot_reload_tx.send(SetRsxMessage { + location: hr, + new_text: rsx, + }); + } } } } } + } else { + last_file_rebuild.insert(path.to_str().unwrap().to_string(), src); } - } else { - last_file_rebuild.insert(path.to_str().unwrap().to_string(), src); + } + } + #[cfg(not(feature = "hot_reload"))] + { + log::info!("reload required"); + if chrono::Local::now().timestamp() > last_update_time { + let _ = reload_tx.send("reload".into()); + last_update_time = chrono::Local::now().timestamp(); } } } @@ -185,7 +207,6 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let router = Router::new() .route("/_dioxus/ws", get(ws_handler)) - .route("/_dioxus/hot_reload", get(hot_reload_handler)) .fallback( get_service(file_service).handle_error(|error: std::io::Error| async move { ( @@ -194,14 +215,16 @@ pub async fn startup(config: CrateConfig) -> Result<()> { ) }), ); + #[cfg(feature = "hot_reload")] + let router = router.route("/_dioxus/hot_reload", get(hot_reload_handler)); + + let router = router.layer(Extension(ws_reload_state)); + + #[cfg(feature = "hot_reload")] + let router = router.layer(Extension(hot_reload_state)); axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) - .serve( - router - .layer(Extension(ws_reload_state)) - .layer(Extension(hot_reload_state)) - .into_make_service(), - ) + .serve(router.into_make_service()) .await?; Ok(()) @@ -232,7 +255,10 @@ async fn ws_handler( { let _ = Serve::regen_dev_page(&state.watcher_config); } - *state.last_file_rebuild.lock().unwrap() = HashMap::new(); + #[cfg(feature = "hot_reload")] + { + *state.last_file_rebuild.lock().unwrap() = HashMap::new(); + } } // ignore the error if socket @@ -250,6 +276,7 @@ async fn ws_handler( }) } +#[cfg(feature = "hot_reload")] async fn hot_reload_handler( ws: WebSocketUpgrade, _: Option>, From f06920a37ec12fafbff53d766c14b89fd8b21c8a Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 12:38:59 -0500 Subject: [PATCH 131/516] search directory .rs files to diff automaticly --- src/server/mod.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 18d50fc57..8670debb1 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,7 +7,10 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; -use std::{fs::File, io::Read}; +use std::{ + fs::{self, File}, + io::{self, Read}, +}; use std::{path::PathBuf, sync::Arc}; use tower::ServiceBuilder; @@ -50,6 +53,8 @@ pub async fn startup(config: CrateConfig) -> Result<()> { #[cfg(feature = "hot_reload")] let last_file_rebuild = Arc::new(Mutex::new(HashMap::new())); #[cfg(feature = "hot_reload")] + find_rs_files(&config.crate_dir, &mut *last_file_rebuild.lock().unwrap()).unwrap(); + #[cfg(feature = "hot_reload")] let hot_reload_tx = broadcast::channel(100).0; #[cfg(feature = "hot_reload")] let hot_reload_state = Arc::new(HotReloadState { @@ -215,6 +220,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { ) }), ); + #[cfg(feature = "hot_reload")] let router = router.route("/_dioxus/hot_reload", get(hot_reload_handler)); @@ -257,7 +263,9 @@ async fn ws_handler( } #[cfg(feature = "hot_reload")] { - *state.last_file_rebuild.lock().unwrap() = HashMap::new(); + let mut write = state.last_file_rebuild.lock().unwrap(); + *write = HashMap::new(); + find_rs_files(&state.watcher_config.crate_dir, &mut *write).unwrap(); } } // ignore the error @@ -311,3 +319,23 @@ async fn hot_reload_handler( } }) } + +#[cfg(feature = "hot_reload")] +fn find_rs_files(root: &PathBuf, files: &mut HashMap) -> io::Result<()> { + for entry in fs::read_dir(root)? { + if let Ok(entry) = entry { + let path = entry.path(); + if path.is_dir() { + find_rs_files(&path, files)?; + } else { + if path.extension().map(|s| s.to_str()).flatten() == Some("rs") { + let mut file = File::open(path.clone()).unwrap(); + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + files.insert(path.display().to_string(), src); + } + } + } + } + Ok(()) +} From aa9f5a1a425c0e07e234ac8e594d041b53fb79f4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 12:44:07 -0500 Subject: [PATCH 132/516] clean up imports --- src/server/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 8670debb1..9c0c86fd6 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,10 +7,7 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; -use std::{ - fs::{self, File}, - io::{self, Read}, -}; +use std::{fs::File, io::Read}; use std::{path::PathBuf, sync::Arc}; use tower::ServiceBuilder; @@ -25,6 +22,7 @@ mod hot_reload_improts { pub use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; pub use std::collections::HashMap; pub use std::sync::Mutex; + pub use std::{fs, io}; pub use syn::__private::ToTokens; } #[cfg(feature = "hot_reload")] From 1d29f66cfc40cbd33065a193cbc013694cfebf0f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 9 Jun 2022 13:00:28 -0500 Subject: [PATCH 133/516] use log::info instead of print --- src/server/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 9c0c86fd6..95026d768 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -289,7 +289,7 @@ async fn hot_reload_handler( Extension(state): Extension>, ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { - println!("🔥 Hot Reload WebSocket is connected."); + log::info!("🔥 Hot Reload WebSocket connected"); let mut rx = state.messages.subscribe(); loop { let read_set_rsx = rx.recv(); From 0c4028ad5270281e0893dd8e919fdebb3407b44c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 10 Jun 2022 19:18:02 -0500 Subject: [PATCH 134/516] close websocket on disconnect --- src/server/mod.rs | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 95026d768..3506112f2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -291,30 +291,40 @@ async fn hot_reload_handler( ws.on_upgrade(|mut socket| async move { log::info!("🔥 Hot Reload WebSocket connected"); let mut rx = state.messages.subscribe(); - loop { - let read_set_rsx = rx.recv(); - let read_err = socket.recv(); - tokio::select! { - err = read_err => { - if let Some(Ok(Message::Text(err))) = err { - let error: RecompileReason = serde_json::from_str(&err).unwrap(); - log::error!("{:?}", error); - state.update.send("reload".to_string()).unwrap(); - }; - }, - set_rsx = read_set_rsx => { - if let Ok(rsx) = set_rsx{ - if socket - .send(Message::Text(serde_json::to_string(&rsx).unwrap())) - .await - .is_err() - { + let hot_reload_handle = tokio::spawn(async move { + loop { + let read_set_rsx = rx.recv(); + let read_err = socket.recv(); + tokio::select! { + err = read_err => { + if let Some(Ok(err)) = err { + if let Message::Text(err) = err { + let error: RecompileReason = serde_json::from_str(&err).unwrap(); + log::error!("{:?}", error); + if state.update.send("reload".to_string()).is_err() { + break; + } + } + } else { break; - }; + } + }, + set_rsx = read_set_rsx => { + if let Ok(rsx) = set_rsx { + if socket + .send(Message::Text(serde_json::to_string(&rsx).unwrap())) + .await + .is_err() + { + break; + }; + } } - } - }; - } + }; + } + }); + + hot_reload_handle.await.unwrap(); }) } From d1c619e5dc9409eb358dfaf141458a5b38edc02b Mon Sep 17 00:00:00 2001 From: xTeKc <81730792+xTeKc@users.noreply.github.com> Date: Sun, 12 Jun 2022 03:31:13 -0400 Subject: [PATCH 135/516] update edition --- rustfmt.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 9071fb4b5..39b98f125 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -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 \ No newline at end of file +format_code_in_doc_comments = true From f287e757611ac2a84a8622bcf9b4a3d3cf60294b Mon Sep 17 00:00:00 2001 From: xTeKc <81730792+xTeKc@users.noreply.github.com> Date: Sun, 12 Jun 2022 03:31:35 -0400 Subject: [PATCH 136/516] update edition --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c869267b1..24493e787 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" From bfab711010dd4f508930c3ce6af8f39c65c6c025 Mon Sep 17 00:00:00 2001 From: xTeKc <81730792+xTeKc@users.noreply.github.com> Date: Sun, 12 Jun 2022 03:35:25 -0400 Subject: [PATCH 137/516] force closure to take ownership --- src/server/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 1df13ca6a..ada3e10c7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -79,7 +79,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let file_service_config = config.clone(); let file_service = ServiceBuilder::new() .and_then( - |response: Response| async move { + move |response: Response| async move { let response = if file_service_config .dioxus_config .web From b3db5469c36b143d024b6b828cebcef261740510 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 13 Jun 2022 15:33:49 -0500 Subject: [PATCH 138/516] ignore files that have not been changed recently when checking for changes initially --- src/hot_reload/mod.rs | 4 +- src/server/mod.rs | 206 +++++++++++++++++++++++++++++------------- 2 files changed, 145 insertions(+), 65 deletions(-) diff --git a/src/hot_reload/mod.rs b/src/hot_reload/mod.rs index 3aced9567..8297fedb9 100644 --- a/src/hot_reload/mod.rs +++ b/src/hot_reload/mod.rs @@ -426,7 +426,9 @@ fn find_rsx_expr( && old_mac.path.get_ident().map(|ident| ident.to_string()) == Some("rsx".to_string()) { - rsx_calls.push((old_mac.clone(), new_mac.tokens.clone())); + if old_mac != new_mac { + rsx_calls.push((old_mac.clone(), new_mac.tokens.clone())); + } false } else { new_expr != old_expr diff --git a/src/server/mod.rs b/src/server/mod.rs index 3506112f2..a0bdf24b0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,7 +7,6 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; -use std::{fs::File, io::Read}; use std::{path::PathBuf, sync::Arc}; use tower::ServiceBuilder; @@ -20,9 +19,12 @@ use tokio::sync::broadcast; mod hot_reload_improts { pub use crate::hot_reload::{find_rsx, DiffResult}; pub use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; + pub use proc_macro2::TokenStream; pub use std::collections::HashMap; pub use std::sync::Mutex; - pub use std::{fs, io}; + pub use std::time::SystemTime; + pub use std::{fs, io, path::Path}; + pub use std::{fs::File, io::Read}; pub use syn::__private::ToTokens; } #[cfg(feature = "hot_reload")] @@ -31,7 +33,7 @@ use hot_reload_improts::*; struct WsReloadState { update: broadcast::Sender, #[cfg(feature = "hot_reload")] - last_file_rebuild: Arc>>, + last_file_rebuild: Arc>, watcher_config: CrateConfig, } @@ -39,6 +41,47 @@ struct WsReloadState { struct HotReloadState { messages: broadcast::Sender, update: broadcast::Sender, + last_file_rebuild: Arc>, + watcher_config: CrateConfig, +} + +#[cfg(feature = "hot_reload")] +struct FileMap { + map: HashMap, + last_updated_time: std::time::SystemTime, +} + +#[cfg(feature = "hot_reload")] +impl FileMap { + fn new(path: PathBuf) -> Self { + fn find_rs_files(root: PathBuf) -> io::Result> { + let mut files = HashMap::new(); + if root.is_dir() { + let mut handles = Vec::new(); + for entry in fs::read_dir(root)? { + if let Ok(entry) = entry { + let path = entry.path(); + handles.push(std::thread::spawn(move || find_rs_files(path))); + } + } + for handle in handles { + files.extend(handle.join().unwrap()?); + } + } else { + if root.extension().map(|s| s.to_str()).flatten() == Some("rs") { + let mut file = File::open(root.clone()).unwrap(); + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + files.insert(root, src); + } + } + Ok(files) + } + Self { + last_updated_time: SystemTime::now(), + map: find_rs_files(path).unwrap(), + } + } } pub async fn startup(config: CrateConfig) -> Result<()> { @@ -49,15 +92,15 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let (reload_tx, _) = broadcast::channel(100); #[cfg(feature = "hot_reload")] - let last_file_rebuild = Arc::new(Mutex::new(HashMap::new())); - #[cfg(feature = "hot_reload")] - find_rs_files(&config.crate_dir, &mut *last_file_rebuild.lock().unwrap()).unwrap(); + let last_file_rebuild = Arc::new(Mutex::new(FileMap::new(config.crate_dir.clone()))); #[cfg(feature = "hot_reload")] let hot_reload_tx = broadcast::channel(100).0; #[cfg(feature = "hot_reload")] let hot_reload_state = Arc::new(HotReloadState { messages: hot_reload_tx.clone(), update: reload_tx.clone(), + last_file_rebuild: last_file_rebuild.clone(), + watcher_config: config.clone(), }); #[cfg(feature = "hot_reload")] let crate_dir = config.crate_dir.clone(); @@ -83,18 +126,21 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { if let Ok(evt) = evt { if let notify::EventKind::Modify(_) = evt.kind { - for path in evt.paths { - let mut file = File::open(path.clone()).unwrap(); - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - if src.is_empty() { - continue; - } - #[cfg(feature = "hot_reload")] - { + #[cfg(feature = "hot_reload")] + { + for path in evt.paths { + let mut file = File::open(path.clone()).unwrap(); + if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { + continue; + } + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + if src.is_empty() { + continue; + } if let Ok(syntax) = syn::parse_file(&src) { let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); - if let Some(old_str) = last_file_rebuild.get(path.to_str().unwrap()) { + if let Some(old_str) = last_file_rebuild.map.get(&path) { if let Ok(old) = syn::parse_file(&old_str) { match find_rsx(&syntax, &old) { DiffResult::CodeChanged => { @@ -107,28 +153,13 @@ pub async fn startup(config: CrateConfig) -> Result<()> { DiffResult::RsxChanged(changed) => { log::info!("reloading rsx"); for (old, new) in changed.into_iter() { - if let Some(hr) = old - .to_token_stream() - .into_iter() - .map(|tree| { - let location = tree.span(); - let start = location.start(); - CodeLocation { - file: path - .strip_prefix(&crate_dir) - .unwrap() - .display() - .to_string(), - line: start.line as u32, - column: start.column as u32 + 1, - } - }) - .min_by(|cl1, cl2| { - cl1.line - .cmp(&cl2.line) - .then(cl1.column.cmp(&cl2.column)) - }) - { + if let Some(hr) = get_min_location( + &path + .strip_prefix(&crate_dir) + .unwrap() + .to_path_buf(), + old.to_token_stream(), + ) { let rsx = new.to_string(); let _ = hot_reload_tx.send(SetRsxMessage { location: hr, @@ -140,17 +171,17 @@ pub async fn startup(config: CrateConfig) -> Result<()> { } } } else { - last_file_rebuild.insert(path.to_str().unwrap().to_string(), src); + *last_file_rebuild = FileMap::new(crate_dir.clone()); } } } - #[cfg(not(feature = "hot_reload"))] - { - log::info!("reload required"); - if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); - } + } + #[cfg(not(feature = "hot_reload"))] + { + log::info!("reload required"); + if chrono::Local::now().timestamp() > last_update_time { + let _ = reload_tx.send("reload".into()); + last_update_time = chrono::Local::now().timestamp(); } } } @@ -262,8 +293,7 @@ async fn ws_handler( #[cfg(feature = "hot_reload")] { let mut write = state.last_file_rebuild.lock().unwrap(); - *write = HashMap::new(); - find_rs_files(&state.watcher_config.crate_dir, &mut *write).unwrap(); + *write = FileMap::new(state.watcher_config.crate_dir.clone()); } } // ignore the error @@ -290,6 +320,59 @@ async fn hot_reload_handler( ) -> impl IntoResponse { ws.on_upgrade(|mut socket| async move { log::info!("🔥 Hot Reload WebSocket connected"); + { + log::info!("Searching files for changes since last run..."); + // update any files that changed before the websocket connected. + let mut messages = Vec::new(); + + { + let handle = state.last_file_rebuild.lock().unwrap(); + let update_time = handle.last_updated_time.clone(); + for (k, v) in handle.map.iter() { + let mut file = File::open(k).unwrap(); + if let Ok(md) = file.metadata() { + if let Ok(time) = md.modified() { + if time < update_time { + continue; + } + } + } + let mut new = String::new(); + file.read_to_string(&mut new).expect("Unable to read file"); + if let Ok(new) = syn::parse_file(&new) { + if let Ok(old) = syn::parse_file(&v) { + if let DiffResult::RsxChanged(changed) = find_rsx(&new, &old) { + for (old, new) in changed.into_iter() { + if let Some(hr) = get_min_location( + k.strip_prefix(&state.watcher_config.crate_dir).unwrap(), + old.to_token_stream(), + ) { + let rsx = new.to_string(); + let msg = SetRsxMessage { + location: hr, + new_text: rsx, + }; + messages.push(msg); + } + } + } + } + } + } + } + + for msg in &messages { + if socket + .send(Message::Text(serde_json::to_string(msg).unwrap())) + .await + .is_err() + { + return; + } + } + log::info!("Updated page"); + } + let mut rx = state.messages.subscribe(); let hot_reload_handle = tokio::spawn(async move { loop { @@ -329,21 +412,16 @@ async fn hot_reload_handler( } #[cfg(feature = "hot_reload")] -fn find_rs_files(root: &PathBuf, files: &mut HashMap) -> io::Result<()> { - for entry in fs::read_dir(root)? { - if let Ok(entry) = entry { - let path = entry.path(); - if path.is_dir() { - find_rs_files(&path, files)?; - } else { - if path.extension().map(|s| s.to_str()).flatten() == Some("rs") { - let mut file = File::open(path.clone()).unwrap(); - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - files.insert(path.display().to_string(), src); - } +fn get_min_location(path: &Path, ts: TokenStream) -> Option { + ts.into_iter() + .map(|tree| { + let location = tree.span(); + let start = location.start(); + CodeLocation { + file: path.display().to_string(), + line: start.line as u32, + column: start.column as u32 + 1, } - } - } - Ok(()) + }) + .min_by(|cl1, cl2| cl1.line.cmp(&cl2.line).then(cl1.column.cmp(&cl2.column))) } From 712d258496415f839b389da8163d5634a36e15f1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 14 Jun 2022 07:31:37 -0500 Subject: [PATCH 139/516] clean up config statements --- src/hot_reload/mod.rs | 4 +- src/server/hot_reload.rs | 174 ++++++++++++++++++ src/server/mod.rs | 383 +++++++++++++++------------------------ 3 files changed, 321 insertions(+), 240 deletions(-) create mode 100644 src/server/hot_reload.rs diff --git a/src/hot_reload/mod.rs b/src/hot_reload/mod.rs index 8297fedb9..3aced9567 100644 --- a/src/hot_reload/mod.rs +++ b/src/hot_reload/mod.rs @@ -426,9 +426,7 @@ fn find_rsx_expr( && old_mac.path.get_ident().map(|ident| ident.to_string()) == Some("rsx".to_string()) { - if old_mac != new_mac { - rsx_calls.push((old_mac.clone(), new_mac.tokens.clone())); - } + rsx_calls.push((old_mac.clone(), new_mac.tokens.clone())); false } else { new_expr != old_expr diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs new file mode 100644 index 000000000..661d249e6 --- /dev/null +++ b/src/server/hot_reload.rs @@ -0,0 +1,174 @@ +use axum::{ + extract::{ws::Message, Extension, TypedHeader, WebSocketUpgrade}, + response::IntoResponse, +}; + +use std::{path::PathBuf, sync::Arc}; + +pub use crate::hot_reload::{find_rsx, DiffResult}; +use crate::CrateConfig; +pub use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; +pub use proc_macro2::TokenStream; +pub use std::collections::HashMap; +pub use std::sync::Mutex; +pub use std::time::SystemTime; +pub use std::{fs, io, path::Path}; +pub use std::{fs::File, io::Read}; +pub use syn::__private::ToTokens; +use tokio::sync::broadcast; + +pub struct HotReloadState { + pub messages: broadcast::Sender, + pub update: broadcast::Sender, + pub last_file_rebuild: Arc>, + pub watcher_config: CrateConfig, +} + +pub struct FileMap { + pub map: HashMap, + pub last_updated_time: std::time::SystemTime, +} + +impl FileMap { + pub fn new(path: PathBuf) -> Self { + fn find_rs_files(root: PathBuf) -> io::Result> { + let mut files = HashMap::new(); + if root.is_dir() { + let mut handles = Vec::new(); + for entry in fs::read_dir(root)? { + if let Ok(entry) = entry { + let path = entry.path(); + handles.push(std::thread::spawn(move || find_rs_files(path))); + } + } + for handle in handles { + files.extend(handle.join().unwrap()?); + } + } else { + if root.extension().map(|s| s.to_str()).flatten() == Some("rs") { + let mut file = File::open(root.clone()).unwrap(); + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + files.insert(root, src); + } + } + Ok(files) + } + Self { + last_updated_time: SystemTime::now(), + map: find_rs_files(path).unwrap(), + } + } +} + +pub async fn hot_reload_handler( + ws: WebSocketUpgrade, + _: Option>, + Extension(state): Extension>, +) -> impl IntoResponse { + ws.on_upgrade(|mut socket| async move { + log::info!("🔥 Hot Reload WebSocket connected"); + { + log::info!("Searching files for changes since last run..."); + // update any files that changed before the websocket connected. + let mut messages = Vec::new(); + + { + let handle = state.last_file_rebuild.lock().unwrap(); + let update_time = handle.last_updated_time.clone(); + for (k, v) in handle.map.iter() { + let mut file = File::open(k).unwrap(); + if let Ok(md) = file.metadata() { + if let Ok(time) = md.modified() { + if time < update_time { + continue; + } + } + } + let mut new = String::new(); + file.read_to_string(&mut new).expect("Unable to read file"); + if let Ok(new) = syn::parse_file(&new) { + if let Ok(old) = syn::parse_file(&v) { + if let DiffResult::RsxChanged(changed) = find_rsx(&new, &old) { + for (old, new) in changed.into_iter() { + if let Some(hr) = get_min_location( + k.strip_prefix(&state.watcher_config.crate_dir).unwrap(), + old.to_token_stream(), + ) { + let rsx = new.to_string(); + let msg = SetRsxMessage { + location: hr, + new_text: rsx, + }; + messages.push(msg); + } + } + } + } + } + } + } + + for msg in &messages { + if socket + .send(Message::Text(serde_json::to_string(msg).unwrap())) + .await + .is_err() + { + return; + } + } + log::info!("Updated page"); + } + + let mut rx = state.messages.subscribe(); + let hot_reload_handle = tokio::spawn(async move { + loop { + let read_set_rsx = rx.recv(); + let read_err = socket.recv(); + tokio::select! { + err = read_err => { + if let Some(Ok(err)) = err { + if let Message::Text(err) = err { + let error: RecompileReason = serde_json::from_str(&err).unwrap(); + log::error!("{:?}", error); + if state.update.send("reload".to_string()).is_err() { + break; + } + } + } else { + break; + } + }, + set_rsx = read_set_rsx => { + if let Ok(rsx) = set_rsx { + if socket + .send(Message::Text(serde_json::to_string(&rsx).unwrap())) + .await + .is_err() + { + break; + }; + } + } + }; + } + }); + + hot_reload_handle.await.unwrap(); + }) +} + +pub fn get_min_location(path: &Path, ts: TokenStream) -> Option { + ts.into_iter() + .map(|tree| { + let location = tree.span(); + let start = location.start(); + CodeLocation { + file: path.display().to_string(), + line: start.line as u32, + column: start.column as u32 + 1, + } + }) + .min_by(|cl1, cl2| cl1.line.cmp(&cl2.line).then(cl1.column.cmp(&cl2.column))) +} diff --git a/src/server/mod.rs b/src/server/mod.rs index a0bdf24b0..262dbf17d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -16,19 +16,9 @@ use crate::{builder, serve::Serve, CrateConfig, Result}; use tokio::sync::broadcast; #[cfg(feature = "hot_reload")] -mod hot_reload_improts { - pub use crate::hot_reload::{find_rsx, DiffResult}; - pub use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; - pub use proc_macro2::TokenStream; - pub use std::collections::HashMap; - pub use std::sync::Mutex; - pub use std::time::SystemTime; - pub use std::{fs, io, path::Path}; - pub use std::{fs::File, io::Read}; - pub use syn::__private::ToTokens; -} +mod hot_reload; #[cfg(feature = "hot_reload")] -use hot_reload_improts::*; +use hot_reload::*; struct WsReloadState { update: broadcast::Sender, @@ -38,76 +28,24 @@ struct WsReloadState { } #[cfg(feature = "hot_reload")] -struct HotReloadState { - messages: broadcast::Sender, - update: broadcast::Sender, - last_file_rebuild: Arc>, - watcher_config: CrateConfig, -} - -#[cfg(feature = "hot_reload")] -struct FileMap { - map: HashMap, - last_updated_time: std::time::SystemTime, -} - -#[cfg(feature = "hot_reload")] -impl FileMap { - fn new(path: PathBuf) -> Self { - fn find_rs_files(root: PathBuf) -> io::Result> { - let mut files = HashMap::new(); - if root.is_dir() { - let mut handles = Vec::new(); - for entry in fs::read_dir(root)? { - if let Ok(entry) = entry { - let path = entry.path(); - handles.push(std::thread::spawn(move || find_rs_files(path))); - } - } - for handle in handles { - files.extend(handle.join().unwrap()?); - } - } else { - if root.extension().map(|s| s.to_str()).flatten() == Some("rs") { - let mut file = File::open(root.clone()).unwrap(); - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - files.insert(root, src); - } - } - Ok(files) - } - Self { - last_updated_time: SystemTime::now(), - map: find_rs_files(path).unwrap(), - } - } -} - pub async fn startup(config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); - let (reload_tx, _) = broadcast::channel(100); - - #[cfg(feature = "hot_reload")] let last_file_rebuild = Arc::new(Mutex::new(FileMap::new(config.crate_dir.clone()))); - #[cfg(feature = "hot_reload")] let hot_reload_tx = broadcast::channel(100).0; - #[cfg(feature = "hot_reload")] let hot_reload_state = Arc::new(HotReloadState { messages: hot_reload_tx.clone(), update: reload_tx.clone(), last_file_rebuild: last_file_rebuild.clone(), watcher_config: config.clone(), }); - #[cfg(feature = "hot_reload")] - let crate_dir = config.crate_dir.clone(); + let crate_dir = config.crate_dir.clone(); let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), - #[cfg(feature = "hot_reload")] + last_file_rebuild: last_file_rebuild.clone(), watcher_config: config.clone(), }); @@ -125,63 +63,49 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { if let Ok(evt) = evt { - if let notify::EventKind::Modify(_) = evt.kind { - #[cfg(feature = "hot_reload")] - { - for path in evt.paths { - let mut file = File::open(path.clone()).unwrap(); - if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { - continue; - } - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - if src.is_empty() { - continue; - } - if let Ok(syntax) = syn::parse_file(&src) { - let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); - if let Some(old_str) = last_file_rebuild.map.get(&path) { - if let Ok(old) = syn::parse_file(&old_str) { - match find_rsx(&syntax, &old) { - DiffResult::CodeChanged => { - log::info!("reload required"); - if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); - } - } - DiffResult::RsxChanged(changed) => { - log::info!("reloading rsx"); - for (old, new) in changed.into_iter() { - if let Some(hr) = get_min_location( - &path - .strip_prefix(&crate_dir) - .unwrap() - .to_path_buf(), - old.to_token_stream(), - ) { - let rsx = new.to_string(); - let _ = hot_reload_tx.send(SetRsxMessage { - location: hr, - new_text: rsx, - }); - } - } + for path in evt.paths { + let mut file = File::open(path.clone()).unwrap(); + if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { + continue; + } + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + if src.is_empty() { + continue; + } + // find changes to the rsx in the file + if let Ok(syntax) = syn::parse_file(&src) { + let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); + if let Some(old_str) = last_file_rebuild.map.get(&path) { + if let Ok(old) = syn::parse_file(&old_str) { + match find_rsx(&syntax, &old) { + DiffResult::CodeChanged => { + log::info!("reload required"); + if chrono::Local::now().timestamp() > last_update_time { + let _ = reload_tx.send("reload".into()); + last_update_time = chrono::Local::now().timestamp(); + } + } + DiffResult::RsxChanged(changed) => { + log::info!("reloading rsx"); + for (old, new) in changed.into_iter() { + if let Some(hr) = get_min_location( + &path.strip_prefix(&crate_dir).unwrap().to_path_buf(), + old.to_token_stream(), + ) { + let rsx = new.to_string(); + let _ = hot_reload_tx.send(SetRsxMessage { + location: hr, + new_text: rsx, + }); } } } - } else { - *last_file_rebuild = FileMap::new(crate_dir.clone()); } } - } - } - #[cfg(not(feature = "hot_reload"))] - { - log::info!("reload required"); - if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); + } else { + // if this is a new file, rebuild the project + *last_file_rebuild = FileMap::new(crate_dir.clone()); } } } @@ -250,13 +174,112 @@ pub async fn startup(config: CrateConfig) -> Result<()> { }), ); - #[cfg(feature = "hot_reload")] - let router = router.route("/_dioxus/hot_reload", get(hot_reload_handler)); + let router = router + .route("/_dioxus/hot_reload", get(hot_reload_handler)) + .layer(Extension(ws_reload_state)) + .layer(Extension(hot_reload_state)); - let router = router.layer(Extension(ws_reload_state)); + axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) + .serve(router.into_make_service()) + .await?; - #[cfg(feature = "hot_reload")] - let router = router.layer(Extension(hot_reload_state)); + Ok(()) +} + +#[cfg(not(feature = "hot_reload"))] +pub async fn startup(config: CrateConfig) -> Result<()> { + log::info!("🚀 Starting development server..."); + + let dist_path = config.out_dir.clone(); + + let (reload_tx, _) = broadcast::channel(100); + + let ws_reload_state = Arc::new(WsReloadState { + update: reload_tx.clone(), + watcher_config: config.clone(), + }); + + let mut last_update_time = chrono::Local::now().timestamp(); + + // file watcher: check file change + let allow_watch_path = config + .dioxus_config + .web + .watcher + .watch_path + .clone() + .unwrap_or_else(|| vec![PathBuf::from("src")]); + + let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { + log::info!("reload required"); + if chrono::Local::now().timestamp() > last_update_time { + let _ = reload_tx.send("reload".into()); + last_update_time = chrono::Local::now().timestamp(); + } + }) + .unwrap(); + + for sub_path in allow_watch_path { + watcher + .watch( + &config.crate_dir.join(sub_path), + notify::RecursiveMode::Recursive, + ) + .unwrap(); + } + + // start serve dev-server at 0.0.0.0:8080 + let port = "8080"; + log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port); + + let file_service_config = config.clone(); + let file_service = ServiceBuilder::new() + .and_then( + |response: Response| async move { + let response = if file_service_config + .dioxus_config + .web + .watcher + .index_on_404 + .unwrap_or(false) + && response.status() == StatusCode::NOT_FOUND + { + let body = Full::from( + // TODO: Cache/memoize this. + std::fs::read_to_string( + file_service_config + .crate_dir + .join(file_service_config.out_dir) + .join("index.html"), + ) + .ok() + .unwrap(), + ) + .map_err(|err| match err {}) + .boxed(); + Response::builder() + .status(StatusCode::OK) + .body(body) + .unwrap() + } else { + response.map(|body| body.boxed()) + }; + Ok(response) + }, + ) + .service(ServeDir::new((&config.crate_dir).join(&dist_path))); + + let router = Router::new() + .route("/_dioxus/ws", get(ws_handler)) + .fallback( + get_service(file_service).handle_error(|error: std::io::Error| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }), + ) + .layer(Extension(ws_reload_state)); axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) .serve(router.into_make_service()) @@ -311,117 +334,3 @@ async fn ws_handler( reload_watcher.await.unwrap(); }) } - -#[cfg(feature = "hot_reload")] -async fn hot_reload_handler( - ws: WebSocketUpgrade, - _: Option>, - Extension(state): Extension>, -) -> impl IntoResponse { - ws.on_upgrade(|mut socket| async move { - log::info!("🔥 Hot Reload WebSocket connected"); - { - log::info!("Searching files for changes since last run..."); - // update any files that changed before the websocket connected. - let mut messages = Vec::new(); - - { - let handle = state.last_file_rebuild.lock().unwrap(); - let update_time = handle.last_updated_time.clone(); - for (k, v) in handle.map.iter() { - let mut file = File::open(k).unwrap(); - if let Ok(md) = file.metadata() { - if let Ok(time) = md.modified() { - if time < update_time { - continue; - } - } - } - let mut new = String::new(); - file.read_to_string(&mut new).expect("Unable to read file"); - if let Ok(new) = syn::parse_file(&new) { - if let Ok(old) = syn::parse_file(&v) { - if let DiffResult::RsxChanged(changed) = find_rsx(&new, &old) { - for (old, new) in changed.into_iter() { - if let Some(hr) = get_min_location( - k.strip_prefix(&state.watcher_config.crate_dir).unwrap(), - old.to_token_stream(), - ) { - let rsx = new.to_string(); - let msg = SetRsxMessage { - location: hr, - new_text: rsx, - }; - messages.push(msg); - } - } - } - } - } - } - } - - for msg in &messages { - if socket - .send(Message::Text(serde_json::to_string(msg).unwrap())) - .await - .is_err() - { - return; - } - } - log::info!("Updated page"); - } - - let mut rx = state.messages.subscribe(); - let hot_reload_handle = tokio::spawn(async move { - loop { - let read_set_rsx = rx.recv(); - let read_err = socket.recv(); - tokio::select! { - err = read_err => { - if let Some(Ok(err)) = err { - if let Message::Text(err) = err { - let error: RecompileReason = serde_json::from_str(&err).unwrap(); - log::error!("{:?}", error); - if state.update.send("reload".to_string()).is_err() { - break; - } - } - } else { - break; - } - }, - set_rsx = read_set_rsx => { - if let Ok(rsx) = set_rsx { - if socket - .send(Message::Text(serde_json::to_string(&rsx).unwrap())) - .await - .is_err() - { - break; - }; - } - } - }; - } - }); - - hot_reload_handle.await.unwrap(); - }) -} - -#[cfg(feature = "hot_reload")] -fn get_min_location(path: &Path, ts: TokenStream) -> Option { - ts.into_iter() - .map(|tree| { - let location = tree.span(); - let start = location.start(); - CodeLocation { - file: path.display().to_string(), - line: start.line as u32, - column: start.column as u32 + 1, - } - }) - .min_by(|cl1, cl2| cl1.line.cmp(&cl2.line).then(cl1.column.cmp(&cl2.column))) -} From fb7ea5915b303fe79682a299bbb6bdc3907e2e82 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 14 Jun 2022 09:47:16 -0500 Subject: [PATCH 140/516] use cli flag instead of feature flag --- Cargo.toml | 8 +++----- src/cli/cfg.rs | 5 +++++ src/cli/serve/mod.rs | 4 +++- src/config.rs | 8 ++++++++ src/lib.rs | 1 - src/server/mod.rs | 26 +++++++++++++++----------- 6 files changed, 34 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 770fd7b1d..7f1223520 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,12 +46,10 @@ flate2 = "1.0.22" tar = "0.4.38" tower = "0.4.12" -syn = { version = "1.0", optional = true } -dioxus-rsx-interpreter = { path = "../dioxus/packages/rsx_interpreter", optional = true } -proc-macro2 = { version = "1.0", features = ["span-locations"], optional = true } +syn = { version = "1.0" } +dioxus-rsx-interpreter = { path = "../dioxus/packages/rsx_interpreter" } +proc-macro2 = { version = "1.0", features = ["span-locations"] } -[features] -hot_reload = ["dioxus-rsx-interpreter", "proc-macro2", "syn"] [[bin]] path = "src/main.rs" diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index f5ee45b87..3f284cbbf 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -39,6 +39,11 @@ pub struct ConfigOptsServe { /// Build platform: support Web & Desktop [default: "default_platform"] #[clap(long)] pub platform: Option, + + /// Build with hot reloading rsx [default: false] + #[clap(long)] + #[serde(default)] + pub hot_reload: bool, } /// Ensure the given value for `--public-url` is formatted correctly. diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 85bd92c74..391e646d4 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -18,7 +18,9 @@ impl Serve { let mut crate_config = crate::CrateConfig::new()?; // change the relase state. - crate_config.with_release(self.serve.release); + crate_config + .with_release(self.serve.release) + .with_hot_reload(self.serve.hot_reload); if self.serve.example.is_some() { crate_config.as_example(self.serve.example.unwrap()); diff --git a/src/config.rs b/src/config.rs index 783c72ccd..aa2a03440 100644 --- a/src/config.rs +++ b/src/config.rs @@ -114,6 +114,7 @@ pub struct CrateConfig { pub executable: ExecutableType, pub dioxus_config: DioxusConfig, pub release: bool, + pub hot_reload: bool, } #[derive(Debug, Clone)] @@ -162,6 +163,7 @@ impl CrateConfig { let executable = ExecutableType::Binary(output_filename); let release = false; + let hot_reload = false; Ok(Self { out_dir, @@ -173,6 +175,7 @@ impl CrateConfig { executable, release, dioxus_config, + hot_reload, }) } @@ -186,6 +189,11 @@ impl CrateConfig { self } + pub fn with_hot_reload(&mut self, hot_reload: bool) -> &mut Self { + self.hot_reload = hot_reload; + self + } + // pub fn with_build_options(&mut self, options: &BuildOptions) { // if let Some(name) = &options.example { // self.as_example(name.clone()); diff --git a/src/lib.rs b/src/lib.rs index 683b7251a..6d009c18c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,5 +19,4 @@ pub use error::*; pub mod logging; pub use logging::*; -#[cfg(feature = "hot_reload")] pub mod hot_reload; diff --git a/src/server/mod.rs b/src/server/mod.rs index 262dbf17d..8a98fd9d9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -15,20 +15,24 @@ use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; use crate::{builder, serve::Serve, CrateConfig, Result}; use tokio::sync::broadcast; -#[cfg(feature = "hot_reload")] mod hot_reload; -#[cfg(feature = "hot_reload")] use hot_reload::*; struct WsReloadState { update: broadcast::Sender, - #[cfg(feature = "hot_reload")] - last_file_rebuild: Arc>, + last_file_rebuild: Option>>, watcher_config: CrateConfig, } -#[cfg(feature = "hot_reload")] pub async fn startup(config: CrateConfig) -> Result<()> { + if config.hot_reload { + startup_hot_reload(config).await + } else { + startup_default(config).await + } +} + +pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); @@ -46,7 +50,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), - last_file_rebuild: last_file_rebuild.clone(), + last_file_rebuild: Some(last_file_rebuild.clone()), watcher_config: config.clone(), }); @@ -186,8 +190,7 @@ pub async fn startup(config: CrateConfig) -> Result<()> { Ok(()) } -#[cfg(not(feature = "hot_reload"))] -pub async fn startup(config: CrateConfig) -> Result<()> { +pub async fn startup_default(config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); @@ -196,6 +199,8 @@ pub async fn startup(config: CrateConfig) -> Result<()> { let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), + + last_file_rebuild: None, watcher_config: config.clone(), }); @@ -313,9 +318,8 @@ async fn ws_handler( { let _ = Serve::regen_dev_page(&state.watcher_config); } - #[cfg(feature = "hot_reload")] - { - let mut write = state.last_file_rebuild.lock().unwrap(); + if let Some(file_map) = &state.last_file_rebuild { + let mut write = file_map.lock().unwrap(); *write = FileMap::new(state.watcher_config.crate_dir.clone()); } } From b0f987aa106994e95af8e75765ce12321684821b Mon Sep 17 00:00:00 2001 From: Demonthos <66571940+Demonthos@users.noreply.github.com> Date: Wed, 15 Jun 2022 12:55:25 -0500 Subject: [PATCH 141/516] accept all interperter errors --- src/server/hot_reload.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 661d249e6..a9f51abe7 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -7,7 +7,7 @@ use std::{path::PathBuf, sync::Arc}; pub use crate::hot_reload::{find_rsx, DiffResult}; use crate::CrateConfig; -pub use dioxus_rsx_interpreter::{error::RecompileReason, CodeLocation, SetRsxMessage}; +pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetRsxMessage}; pub use proc_macro2::TokenStream; pub use std::collections::HashMap; pub use std::sync::Mutex; @@ -130,7 +130,7 @@ pub async fn hot_reload_handler( err = read_err => { if let Some(Ok(err)) = err { if let Message::Text(err) = err { - let error: RecompileReason = serde_json::from_str(&err).unwrap(); + let error: Error = serde_json::from_str(&err).unwrap(); log::error!("{:?}", error); if state.update.send("reload".to_string()).is_err() { break; From 8ff50df3a5dc14b90b15fd84122a12436326144a Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 15 Jun 2022 13:29:35 -0500 Subject: [PATCH 142/516] preserve whitespace in rsx --- src/server/hot_reload.rs | 37 ++++++++++++++++--------------------- src/server/mod.rs | 29 ++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index a9f51abe7..f79a7a50e 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -15,6 +15,7 @@ pub use std::time::SystemTime; pub use std::{fs, io, path::Path}; pub use std::{fs::File, io::Read}; pub use syn::__private::ToTokens; +use syn::spanned::Spanned; use tokio::sync::broadcast; pub struct HotReloadState { @@ -91,17 +92,16 @@ pub async fn hot_reload_handler( if let Ok(old) = syn::parse_file(&v) { if let DiffResult::RsxChanged(changed) = find_rsx(&new, &old) { for (old, new) in changed.into_iter() { - if let Some(hr) = get_min_location( + let hr = get_location( k.strip_prefix(&state.watcher_config.crate_dir).unwrap(), old.to_token_stream(), - ) { - let rsx = new.to_string(); - let msg = SetRsxMessage { - location: hr, - new_text: rsx, - }; - messages.push(msg); - } + ); + let rsx = new.to_string(); + let msg = SetRsxMessage { + location: hr, + new_text: rsx, + }; + messages.push(msg); } } } @@ -159,16 +159,11 @@ pub async fn hot_reload_handler( }) } -pub fn get_min_location(path: &Path, ts: TokenStream) -> Option { - ts.into_iter() - .map(|tree| { - let location = tree.span(); - let start = location.start(); - CodeLocation { - file: path.display().to_string(), - line: start.line as u32, - column: start.column as u32 + 1, - } - }) - .min_by(|cl1, cl2| cl1.line.cmp(&cl2.line).then(cl1.column.cmp(&cl2.column))) +pub fn get_location(path: &Path, ts: TokenStream) -> CodeLocation { + let span = ts.span().start(); + CodeLocation { + file: path.display().to_string(), + line: span.line as u32, + column: span.column as u32 + 1, + } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 8a98fd9d9..3397beac6 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -7,6 +7,7 @@ use axum::{ Router, }; use notify::{RecommendedWatcher, Watcher}; +use syn::spanned::Spanned; use std::{path::PathBuf, sync::Arc}; use tower::ServiceBuilder; @@ -93,16 +94,30 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { DiffResult::RsxChanged(changed) => { log::info!("reloading rsx"); for (old, new) in changed.into_iter() { - if let Some(hr) = get_min_location( + let hr = get_location( &path.strip_prefix(&crate_dir).unwrap().to_path_buf(), old.to_token_stream(), - ) { - let rsx = new.to_string(); - let _ = hot_reload_tx.send(SetRsxMessage { - location: hr, - new_text: rsx, - }); + ); + // get the original source code to preserve whitespace + let span = new.span(); + let start = span.start(); + let end = span.end(); + let mut lines: Vec<_> = src + .lines() + .skip(start.line - 1) + .take(end.line - start.line + 1) + .collect(); + if let Some(first) = lines.first_mut() { + *first = first.split_at(start.column).1; } + if let Some(last) = lines.last_mut() { + *last = last.split_at(end.column).0; + } + let rsx = lines.join("\n"); + let _ = hot_reload_tx.send(SetRsxMessage { + location: hr, + new_text: rsx, + }); } } } From edc31dfc031025a6a7893da39fe3278fa053d2c7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 15 Jun 2022 13:58:24 -0500 Subject: [PATCH 143/516] better error messages --- src/server/hot_reload.rs | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index f79a7a50e..2011b8958 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -86,22 +86,36 @@ pub async fn hot_reload_handler( } } } - let mut new = String::new(); - file.read_to_string(&mut new).expect("Unable to read file"); - if let Ok(new) = syn::parse_file(&new) { - if let Ok(old) = syn::parse_file(&v) { - if let DiffResult::RsxChanged(changed) = find_rsx(&new, &old) { + let mut new_str = String::new(); + file.read_to_string(&mut new_str).expect("Unable to read file"); + if let Ok(new_file) = syn::parse_file(&new_str) { + if let Ok(old_file) = syn::parse_file(&v) { + if let DiffResult::RsxChanged(changed) = find_rsx(&new_file, &old_file) { for (old, new) in changed.into_iter() { let hr = get_location( k.strip_prefix(&state.watcher_config.crate_dir).unwrap(), old.to_token_stream(), ); - let rsx = new.to_string(); - let msg = SetRsxMessage { + // get the original source code to preserve whitespace + let span = new.span(); + let start = span.start(); + let end = span.end(); + let mut lines: Vec<_> = new_str + .lines() + .skip(start.line - 1) + .take(end.line - start.line + 1) + .collect(); + if let Some(first) = lines.first_mut() { + *first = first.split_at(start.column).1; + } + if let Some(last) = lines.last_mut() { + *last = last.split_at(end.column).0; + } + let rsx = lines.join("\n"); + messages.push(SetRsxMessage { location: hr, new_text: rsx, - }; - messages.push(msg); + }); } } } @@ -131,9 +145,16 @@ pub async fn hot_reload_handler( if let Some(Ok(err)) = err { if let Message::Text(err) = err { let error: Error = serde_json::from_str(&err).unwrap(); - log::error!("{:?}", error); - if state.update.send("reload".to_string()).is_err() { - break; + match error{ + Error::ParseError(parse_error) => { + log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file, parse_error.location.line, parse_error.location.column, parse_error.message); + }, + Error::RecompileRequiredError(reason) => { + log::info!("{:?}", reason); + if state.update.send("reload".to_string()).is_err() { + break; + } + } } } } else { From 8e33b8e02711cc4108f4d05d2cf95d647ad697d7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 16 Jun 2022 09:11:02 -0500 Subject: [PATCH 144/516] improve speed when reloading the site --- src/server/hot_reload.rs | 91 +++++++++++++++++++++-------- src/server/mod.rs | 120 +++++++++++++++++++++------------------ 2 files changed, 132 insertions(+), 79 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 2011b8958..14f9ffbc6 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -2,12 +2,13 @@ use axum::{ extract::{ws::Message, Extension, TypedHeader, WebSocketUpgrade}, response::IntoResponse, }; +use dioxus_rsx_interpreter::SetRsxMessage; use std::{path::PathBuf, sync::Arc}; pub use crate::hot_reload::{find_rsx, DiffResult}; use crate::CrateConfig; -pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetRsxMessage}; +pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetManyRsxMessage}; pub use proc_macro2::TokenStream; pub use std::collections::HashMap; pub use std::sync::Mutex; @@ -19,7 +20,7 @@ use syn::spanned::Spanned; use tokio::sync::broadcast; pub struct HotReloadState { - pub messages: broadcast::Sender, + pub messages: broadcast::Sender, pub update: broadcast::Sender, pub last_file_rebuild: Arc>, pub watcher_config: CrateConfig, @@ -32,32 +33,76 @@ pub struct FileMap { impl FileMap { pub fn new(path: PathBuf) -> Self { + log::info!("Searching files for changes since last compile..."); fn find_rs_files(root: PathBuf) -> io::Result> { let mut files = HashMap::new(); if root.is_dir() { - let mut handles = Vec::new(); for entry in fs::read_dir(root)? { if let Ok(entry) = entry { let path = entry.path(); - handles.push(std::thread::spawn(move || find_rs_files(path))); + files.extend(find_rs_files(path)?); } } - for handle in handles { - files.extend(handle.join().unwrap()?); - } } else { if root.extension().map(|s| s.to_str()).flatten() == Some("rs") { - let mut file = File::open(root.clone()).unwrap(); - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - files.insert(root, src); + if let Ok(mut file) = File::open(root.clone()) { + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + files.insert(root, src); + } } } Ok(files) } - Self { - last_updated_time: SystemTime::now(), + + let last_updated_time = SystemTime::now(); + let result = Self { + last_updated_time, map: find_rs_files(path).unwrap(), + }; + log::info!("Files updated"); + result + } + + pub fn update(&mut self, path: PathBuf) { + log::info!("Updating files that changed since last compile..."); + self.last_updated_time = SystemTime::now(); + self.update_inner(path); + log::info!("Files updated"); + } + + fn update_inner(&mut self, path: PathBuf) { + if path.is_dir() { + if let Ok(files) = fs::read_dir(path) { + for entry in files { + if let Ok(entry) = entry { + if let Ok(md) = entry.metadata() { + if let Ok(time) = md.modified() { + if time < self.last_updated_time { + return; + } + } + } + let path = entry.path(); + self.update(path); + } + } + } + } else { + if path.extension().map(|s| s.to_str()).flatten() == Some("rs") { + if let Ok(mut file) = File::open(path.clone()) { + if let Ok(md) = file.metadata() { + if let Ok(time) = md.modified() { + if time < self.last_updated_time { + return; + } + } + } + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + self.map.insert(path, src); + } + } } } } @@ -70,7 +115,6 @@ pub async fn hot_reload_handler( ws.on_upgrade(|mut socket| async move { log::info!("🔥 Hot Reload WebSocket connected"); { - log::info!("Searching files for changes since last run..."); // update any files that changed before the websocket connected. let mut messages = Vec::new(); @@ -123,16 +167,14 @@ pub async fn hot_reload_handler( } } - for msg in &messages { - if socket - .send(Message::Text(serde_json::to_string(msg).unwrap())) - .await - .is_err() - { - return; - } + let msg = SetManyRsxMessage(messages); + if socket + .send(Message::Text(serde_json::to_string(&msg).unwrap())) + .await + .is_err() + { + return; } - log::info!("Updated page"); } let mut rx = state.messages.subscribe(); @@ -149,8 +191,7 @@ pub async fn hot_reload_handler( Error::ParseError(parse_error) => { log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file, parse_error.location.line, parse_error.location.column, parse_error.message); }, - Error::RecompileRequiredError(reason) => { - log::info!("{:?}", reason); + Error::RecompileRequiredError(_) => { if state.update.send("reload".to_string()).is_err() { break; } diff --git a/src/server/mod.rs b/src/server/mod.rs index 3397beac6..19791fd79 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,6 +6,7 @@ use axum::{ routing::{get, get_service}, Router, }; +use dioxus_rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; use syn::spanned::Spanned; @@ -67,67 +68,75 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { .unwrap_or_else(|| vec![PathBuf::from("src")]); let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { - if let Ok(evt) = evt { - for path in evt.paths { - let mut file = File::open(path.clone()).unwrap(); - if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { - continue; - } - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - if src.is_empty() { - continue; - } - // find changes to the rsx in the file - if let Ok(syntax) = syn::parse_file(&src) { - let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); - if let Some(old_str) = last_file_rebuild.map.get(&path) { - if let Ok(old) = syn::parse_file(&old_str) { - match find_rsx(&syntax, &old) { - DiffResult::CodeChanged => { - log::info!("reload required"); - if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); + if chrono::Local::now().timestamp() > last_update_time { + if let Ok(evt) = evt { + let mut messages = Vec::new(); + for path in evt.paths { + let mut file = File::open(path.clone()).unwrap(); + if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { + continue; + } + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + if src.is_empty() { + continue; + } + // find changes to the rsx in the file + if let Ok(syntax) = syn::parse_file(&src) { + let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); + if let Some(old_str) = last_file_rebuild.map.get(&path) { + if let Ok(old) = syn::parse_file(&old_str) { + match find_rsx(&syntax, &old) { + DiffResult::CodeChanged => { + log::info!("reload required"); + let _ = reload_tx.send("reload time".into()); + break; } - } - DiffResult::RsxChanged(changed) => { - log::info!("reloading rsx"); - for (old, new) in changed.into_iter() { - let hr = get_location( - &path.strip_prefix(&crate_dir).unwrap().to_path_buf(), - old.to_token_stream(), - ); - // get the original source code to preserve whitespace - let span = new.span(); - let start = span.start(); - let end = span.end(); - let mut lines: Vec<_> = src - .lines() - .skip(start.line - 1) - .take(end.line - start.line + 1) - .collect(); - if let Some(first) = lines.first_mut() { - *first = first.split_at(start.column).1; + DiffResult::RsxChanged(changed) => { + log::info!("reloading rsx"); + for (old, new) in changed.into_iter() { + let hr = get_location( + &path + .strip_prefix(&crate_dir) + .unwrap() + .to_path_buf(), + old.to_token_stream(), + ); + // get the original source code to preserve whitespace + let span = new.span(); + let start = span.start(); + let end = span.end(); + let mut lines: Vec<_> = src + .lines() + .skip(start.line - 1) + .take(end.line - start.line + 1) + .collect(); + if let Some(first) = lines.first_mut() { + *first = first.split_at(start.column).1; + } + if let Some(last) = lines.last_mut() { + *last = last.split_at(end.column).0; + } + let rsx = lines.join("\n"); + messages.push(SetRsxMessage { + location: hr, + new_text: rsx, + }); } - if let Some(last) = lines.last_mut() { - *last = last.split_at(end.column).0; - } - let rsx = lines.join("\n"); - let _ = hot_reload_tx.send(SetRsxMessage { - location: hr, - new_text: rsx, - }); } } } + } else { + // if this is a new file, rebuild the project + *last_file_rebuild = FileMap::new(crate_dir.clone()); } - } else { - // if this is a new file, rebuild the project - *last_file_rebuild = FileMap::new(crate_dir.clone()); } } + if !messages.is_empty() { + let _ = hot_reload_tx.send(SetManyRsxMessage(messages)); + } } + last_update_time = chrono::Local::now().timestamp(); } }) .unwrap(); @@ -313,7 +322,7 @@ async fn ws_handler( _: Option>, Extension(state): Extension>, ) -> impl IntoResponse { - ws.on_upgrade(|mut socket| async move { + ws.max_send_queue(1).on_upgrade(|mut socket| async move { let mut rx = state.update.subscribe(); let reload_watcher = tokio::spawn(async move { loop { @@ -335,7 +344,7 @@ async fn ws_handler( } if let Some(file_map) = &state.last_file_rebuild { let mut write = file_map.lock().unwrap(); - *write = FileMap::new(state.watcher_config.crate_dir.clone()); + write.update(state.watcher_config.crate_dir.clone()); } } // ignore the error @@ -346,6 +355,9 @@ async fn ws_handler( { break; } + + // flush the errors after recompling + rx = rx.resubscribe(); } } }); From 22e40c6bc1f35f0a3c9a6ec9d225f7c4fe029fff Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 16 Jun 2022 10:21:28 -0500 Subject: [PATCH 145/516] removed debugging messages --- src/server/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 19791fd79..bf9299022 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -89,7 +89,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { match find_rsx(&syntax, &old) { DiffResult::CodeChanged => { log::info!("reload required"); - let _ = reload_tx.send("reload time".into()); + let _ = reload_tx.send("reload".into()); break; } DiffResult::RsxChanged(changed) => { From a79046f36a39a20cc4e43f8e3d83df46e23b4fa5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 16 Jun 2022 13:13:06 -0500 Subject: [PATCH 146/516] use build manager instead of rebuilding in the ws handler --- src/server/hot_reload.rs | 53 +++--------------- src/server/mod.rs | 115 ++++++++++++++++++++++----------------- 2 files changed, 72 insertions(+), 96 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 14f9ffbc6..6a2a58ba0 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -6,6 +6,7 @@ use dioxus_rsx_interpreter::SetRsxMessage; use std::{path::PathBuf, sync::Arc}; +use super::BuildManager; pub use crate::hot_reload::{find_rsx, DiffResult}; use crate::CrateConfig; pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetManyRsxMessage}; @@ -21,7 +22,7 @@ use tokio::sync::broadcast; pub struct HotReloadState { pub messages: broadcast::Sender, - pub update: broadcast::Sender, + pub build_manager: Arc, pub last_file_rebuild: Arc>, pub watcher_config: CrateConfig, } @@ -63,48 +64,6 @@ impl FileMap { log::info!("Files updated"); result } - - pub fn update(&mut self, path: PathBuf) { - log::info!("Updating files that changed since last compile..."); - self.last_updated_time = SystemTime::now(); - self.update_inner(path); - log::info!("Files updated"); - } - - fn update_inner(&mut self, path: PathBuf) { - if path.is_dir() { - if let Ok(files) = fs::read_dir(path) { - for entry in files { - if let Ok(entry) = entry { - if let Ok(md) = entry.metadata() { - if let Ok(time) = md.modified() { - if time < self.last_updated_time { - return; - } - } - } - let path = entry.path(); - self.update(path); - } - } - } - } else { - if path.extension().map(|s| s.to_str()).flatten() == Some("rs") { - if let Ok(mut file) = File::open(path.clone()) { - if let Ok(md) = file.metadata() { - if let Ok(time) = md.modified() { - if time < self.last_updated_time { - return; - } - } - } - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - self.map.insert(path, src); - } - } - } - } } pub async fn hot_reload_handler( @@ -115,10 +74,11 @@ pub async fn hot_reload_handler( ws.on_upgrade(|mut socket| async move { log::info!("🔥 Hot Reload WebSocket connected"); { - // update any files that changed before the websocket connected. + // update any rsx calls that changed before the websocket connected. let mut messages = Vec::new(); { + println!("Finding updates since last reload..."); let handle = state.last_file_rebuild.lock().unwrap(); let update_time = handle.last_updated_time.clone(); for (k, v) in handle.map.iter() { @@ -165,6 +125,7 @@ pub async fn hot_reload_handler( } } } + println!("finished"); } let msg = SetManyRsxMessage(messages); @@ -192,8 +153,8 @@ pub async fn hot_reload_handler( log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file, parse_error.location.line, parse_error.location.column, parse_error.message); }, Error::RecompileRequiredError(_) => { - if state.update.send("reload".to_string()).is_err() { - break; + if let Err(err) = state.build_manager.build(){ + log::error!("{}", err); } } } diff --git a/src/server/mod.rs b/src/server/mod.rs index bf9299022..5742c86ee 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -20,18 +20,43 @@ use tokio::sync::broadcast; mod hot_reload; use hot_reload::*; +pub struct BuildManager { + config: CrateConfig, + reload_tx: broadcast::Sender<()>, +} + +impl BuildManager { + fn build(&self) -> Result<()> { + log::info!("Start to rebuild project..."); + builder::build(&self.config)?; + // change the websocket reload state to true; + // the page will auto-reload. + if self + .config + .dioxus_config + .web + .watcher + .reload_html + .unwrap_or(false) + { + let _ = Serve::regen_dev_page(&self.config); + } + let _ = self.reload_tx.send(()); + Ok(()) + } +} + struct WsReloadState { - update: broadcast::Sender, - last_file_rebuild: Option>>, - watcher_config: CrateConfig, + update: broadcast::Sender<()>, } pub async fn startup(config: CrateConfig) -> Result<()> { if config.hot_reload { - startup_hot_reload(config).await + startup_hot_reload(config).await? } else { - startup_default(config).await + startup_default(config).await? } + Ok(()) } pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { @@ -40,10 +65,14 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { let dist_path = config.out_dir.clone(); let (reload_tx, _) = broadcast::channel(100); let last_file_rebuild = Arc::new(Mutex::new(FileMap::new(config.crate_dir.clone()))); + let build_manager = Arc::new(BuildManager { + config: config.clone(), + reload_tx: reload_tx.clone(), + }); let hot_reload_tx = broadcast::channel(100).0; let hot_reload_state = Arc::new(HotReloadState { messages: hot_reload_tx.clone(), - update: reload_tx.clone(), + build_manager: build_manager.clone(), last_file_rebuild: last_file_rebuild.clone(), watcher_config: config.clone(), }); @@ -51,9 +80,6 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { let crate_dir = config.crate_dir.clone(); let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), - - last_file_rebuild: Some(last_file_rebuild.clone()), - watcher_config: config.clone(), }); let mut last_update_time = chrono::Local::now().timestamp(); @@ -71,6 +97,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { if chrono::Local::now().timestamp() > last_update_time { if let Ok(evt) = evt { let mut messages = Vec::new(); + let mut needs_rebuild = false; for path in evt.paths { let mut file = File::open(path.clone()).unwrap(); if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { @@ -88,9 +115,8 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { if let Ok(old) = syn::parse_file(&old_str) { match find_rsx(&syntax, &old) { DiffResult::CodeChanged => { - log::info!("reload required"); - let _ = reload_tx.send("reload".into()); - break; + needs_rebuild = true; + last_file_rebuild.map.insert(path, src); } DiffResult::RsxChanged(changed) => { log::info!("reloading rsx"); @@ -132,6 +158,12 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { } } } + if needs_rebuild { + log::info!("reload required"); + if let Err(err) = build_manager.build() { + log::error!("{}", err); + } + } if !messages.is_empty() { let _ = hot_reload_tx.send(SetManyRsxMessage(messages)); } @@ -221,11 +253,13 @@ pub async fn startup_default(config: CrateConfig) -> Result<()> { let (reload_tx, _) = broadcast::channel(100); + let build_manager = BuildManager { + config: config.clone(), + reload_tx: reload_tx.clone(), + }; + let ws_reload_state = Arc::new(WsReloadState { update: reload_tx.clone(), - - last_file_rebuild: None, - watcher_config: config.clone(), }); let mut last_update_time = chrono::Local::now().timestamp(); @@ -242,8 +276,10 @@ pub async fn startup_default(config: CrateConfig) -> Result<()> { let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { log::info!("reload required"); if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); + match build_manager.build() { + Ok(_) => last_update_time = chrono::Local::now().timestamp(), + Err(e) => log::error!("{}", e), + } } }) .unwrap(); @@ -322,43 +358,22 @@ async fn ws_handler( _: Option>, Extension(state): Extension>, ) -> impl IntoResponse { - ws.max_send_queue(1).on_upgrade(|mut socket| async move { + ws.on_upgrade(|mut socket| async move { let mut rx = state.update.subscribe(); let reload_watcher = tokio::spawn(async move { loop { - let v = rx.recv().await.unwrap(); - if v == "reload" { - log::info!("Start to rebuild project..."); - if builder::build(&state.watcher_config).is_ok() { - // change the websocket reload state to true; - // the page will auto-reload. - if state - .watcher_config - .dioxus_config - .web - .watcher - .reload_html - .unwrap_or(false) - { - let _ = Serve::regen_dev_page(&state.watcher_config); - } - if let Some(file_map) = &state.last_file_rebuild { - let mut write = file_map.lock().unwrap(); - write.update(state.watcher_config.crate_dir.clone()); - } - } - // ignore the error - if socket - .send(Message::Text(String::from("reload"))) - .await - .is_err() - { - break; - } - - // flush the errors after recompling - rx = rx.resubscribe(); + rx.recv().await.unwrap(); + // ignore the error + if socket + .send(Message::Text(String::from("reload"))) + .await + .is_err() + { + break; } + + // flush the errors after recompling + rx = rx.resubscribe(); } }); From 815bdcdf0032a37518ee1611d9b0cd08bd2c79f3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 16 Jun 2022 13:23:45 -0500 Subject: [PATCH 147/516] clean up logs --- src/server/hot_reload.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 6a2a58ba0..a0eb43e08 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -78,7 +78,7 @@ pub async fn hot_reload_handler( let mut messages = Vec::new(); { - println!("Finding updates since last reload..."); + log::info!("Finding updates since last compile..."); let handle = state.last_file_rebuild.lock().unwrap(); let update_time = handle.last_updated_time.clone(); for (k, v) in handle.map.iter() { @@ -125,7 +125,7 @@ pub async fn hot_reload_handler( } } } - println!("finished"); + log::info!("finished"); } let msg = SetManyRsxMessage(messages); From 2908e53d1b55d6d5664d1b27046d53966d42bd5e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 17 Jun 2022 14:50:05 -0500 Subject: [PATCH 148/516] add delay before reading the changed file --- src/server/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 1eb159ae5..16401aa85 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -95,6 +95,8 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { if chrono::Local::now().timestamp() > last_update_time { + // Give time for the change to take effect before reading the file + std::thread::sleep(std::time::Duration::from_millis(100)); if let Ok(evt) = evt { let mut messages = Vec::new(); let mut needs_rebuild = false; @@ -105,9 +107,6 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { } let mut src = String::new(); file.read_to_string(&mut src).expect("Unable to read file"); - if src.is_empty() { - continue; - } // find changes to the rsx in the file if let Ok(syntax) = syn::parse_file(&src) { let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); From 7025bd43aab4e98c8fe1b5907f51c19117efc814 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 20 Jun 2022 15:44:14 -0500 Subject: [PATCH 149/516] use line!, col!, and row! instead of panic location --- src/server/hot_reload.rs | 2 +- src/server/mod.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index a0eb43e08..7c9a6957e 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -97,7 +97,7 @@ pub async fn hot_reload_handler( if let DiffResult::RsxChanged(changed) = find_rsx(&new_file, &old_file) { for (old, new) in changed.into_iter() { let hr = get_location( - k.strip_prefix(&state.watcher_config.crate_dir).unwrap(), + k, old.to_token_stream(), ); // get the original source code to preserve whitespace diff --git a/src/server/mod.rs b/src/server/mod.rs index 16401aa85..8cdd76373 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -121,10 +121,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { log::info!("reloading rsx"); for (old, new) in changed.into_iter() { let hr = get_location( - &path - .strip_prefix(&crate_dir) - .unwrap() - .to_path_buf(), + &path.to_path_buf(), old.to_token_stream(), ); // get the original source code to preserve whitespace From f8014f307b7ae0ac53a9415e4849d35b4a286fc3 Mon Sep 17 00:00:00 2001 From: Demonthos <66571940+Demonthos@users.noreply.github.com> Date: Tue, 21 Jun 2022 10:19:42 -0500 Subject: [PATCH 150/516] depend on dioxus master until next release --- Cargo.toml | 2 +- src/server/hot_reload.rs | 4 ++-- src/server/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f5afe80db..59a840d77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ zip = "0.6.2" tower = "0.4.12" syn = { version = "1.0" } -dioxus-rsx-interpreter = { path = "../dioxus/packages/rsx_interpreter" } +dioxus = { git = "https://github.com/dioxuslabs/dioxus/", features = ["hot_reload"] } proc-macro2 = { version = "1.0", features = ["span-locations"] } diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 7c9a6957e..ea9c64fc7 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -2,14 +2,14 @@ use axum::{ extract::{ws::Message, Extension, TypedHeader, WebSocketUpgrade}, response::IntoResponse, }; -use dioxus_rsx_interpreter::SetRsxMessage; +use dioxus::rsx_interpreter::SetRsxMessage; use std::{path::PathBuf, sync::Arc}; use super::BuildManager; pub use crate::hot_reload::{find_rsx, DiffResult}; use crate::CrateConfig; -pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetManyRsxMessage}; +pub use dioxus::rsx_interpreter::{error::Error, CodeLocation, SetManyRsxMessage}; pub use proc_macro2::TokenStream; pub use std::collections::HashMap; pub use std::sync::Mutex; diff --git a/src/server/mod.rs b/src/server/mod.rs index 8cdd76373..387355ee9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,7 +6,7 @@ use axum::{ routing::{get, get_service}, Router, }; -use dioxus_rsx_interpreter::SetRsxMessage; +use dioxus::rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; use syn::spanned::Spanned; From abded864c315400c1b7b0b1f80af9bf182a2b9c1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 09:59:05 -0500 Subject: [PATCH 151/516] update hot_reload to hot-reload --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 59a840d77..42de2ba4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ zip = "0.6.2" tower = "0.4.12" syn = { version = "1.0" } -dioxus = { git = "https://github.com/dioxuslabs/dioxus/", features = ["hot_reload"] } +dioxus = { git = "https://github.com/dioxuslabs/dioxus/", features = ["hot-reload"] } proc-macro2 = { version = "1.0", features = ["span-locations"] } From c9ac828ec773ad37f5fa5c12cfc610e9506d6ab8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Jun 2022 10:00:02 -0500 Subject: [PATCH 152/516] fix path seperator --- src/server/hot_reload.rs | 9 ++++++--- src/server/mod.rs | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index ea9c64fc7..d34f079f5 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -97,6 +97,7 @@ pub async fn hot_reload_handler( if let DiffResult::RsxChanged(changed) = find_rsx(&new_file, &old_file) { for (old, new) in changed.into_iter() { let hr = get_location( + &state.watcher_config.crate_dir, k, old.to_token_stream(), ); @@ -150,7 +151,7 @@ pub async fn hot_reload_handler( let error: Error = serde_json::from_str(&err).unwrap(); match error{ Error::ParseError(parse_error) => { - log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file, 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(_) => { if let Err(err) = state.build_manager.build(){ @@ -182,10 +183,12 @@ pub async fn hot_reload_handler( }) } -pub fn get_location(path: &Path, ts: TokenStream) -> CodeLocation { +pub fn get_location(crate_path: &Path, path: &Path, ts: TokenStream) -> CodeLocation { let span = ts.span().start(); + let relative = path.strip_prefix(crate_path).unwrap(); CodeLocation { - file: path.display().to_string(), + file_path: relative.display().to_string(), + crate_path: crate_path.display().to_string(), line: span.line as u32, column: span.column as u32 + 1, } diff --git a/src/server/mod.rs b/src/server/mod.rs index 387355ee9..4bb08170e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -121,6 +121,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { log::info!("reloading rsx"); for (old, new) in changed.into_iter() { let hr = get_location( + &crate_dir, &path.to_path_buf(), old.to_token_stream(), ); From c54d4677302a6bafcd9185fd3340fb59f94c6047 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 29 Jun 2022 16:40:23 +0800 Subject: [PATCH 153/516] feat: set `port` configurable --- src/cli/cfg.rs | 5 +++++ src/cli/serve/mod.rs | 2 +- src/server/mod.rs | 14 ++++++-------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 73f9570d4..9fe453c49 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -40,6 +40,11 @@ pub struct ConfigOptsServe { #[clap(parse(from_os_str))] pub target: Option, + /// Port of dev server + #[clap(long)] + #[clap(default_value_t = 8080)] + pub port: u16, + /// Build a example [default: ""] #[clap(long)] pub example: Option, diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 5511845cb..5528ebade 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -69,7 +69,7 @@ impl Serve { Serve::regen_dev_page(&crate_config)?; // start the develop server - server::startup(crate_config.clone()).await?; + server::startup(self.serve.port, crate_config.clone()).await?; Ok(()) } diff --git a/src/server/mod.rs b/src/server/mod.rs index 387355ee9..096b07cc0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -50,16 +50,16 @@ struct WsReloadState { update: broadcast::Sender<()>, } -pub async fn startup(config: CrateConfig) -> Result<()> { +pub async fn startup(port: u16, config: CrateConfig) -> Result<()> { if config.hot_reload { - startup_hot_reload(config).await? + startup_hot_reload(port, config).await? } else { - startup_default(config).await? + startup_default(port, config).await? } Ok(()) } -pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { +pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); @@ -179,7 +179,6 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0:8080 - let port = "8080"; log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port); let file_service_config = config.clone(); @@ -242,7 +241,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { Ok(()) } -pub async fn startup_default(config: CrateConfig) -> Result<()> { +pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); @@ -289,8 +288,7 @@ pub async fn startup_default(config: CrateConfig) -> Result<()> { .unwrap(); } - // start serve dev-server at 0.0.0.0:8080 - let port = "8080"; + // start serve dev-server at 0.0.0.0 log::info!("📡 Dev-Server is started at: http://127.0.0.1:{}/", port); let file_service_config = config.clone(); From f4ed207649e8dbcb36092f5db58834edded7c0de Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 00:22:40 +0800 Subject: [PATCH 154/516] feat: commit code --- src/server/hot_reload.rs | 6 +++--- src/server/mod.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index ea9c64fc7..6f494dfc7 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -34,7 +34,7 @@ pub struct FileMap { impl FileMap { 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> { let mut files = HashMap::new(); if root.is_dir() { @@ -61,7 +61,7 @@ impl FileMap { last_updated_time, map: find_rs_files(path).unwrap(), }; - log::info!("Files updated"); + // log::info!("Files updated"); result } } @@ -78,7 +78,7 @@ pub async fn hot_reload_handler( 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 update_time = handle.last_updated_time.clone(); for (k, v) in handle.map.iter() { diff --git a/src/server/mod.rs b/src/server/mod.rs index 096b07cc0..75134a1ac 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -27,7 +27,7 @@ pub struct BuildManager { impl BuildManager { fn build(&self) -> Result<()> { - log::info!("Start to rebuild project..."); + log::info!("🪁 Rebuild code"); builder::build(&self.config)?; // change the websocket reload state to true; // the page will auto-reload. @@ -118,7 +118,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { last_file_rebuild.map.insert(path, src); } DiffResult::RsxChanged(changed) => { - log::info!("reloading rsx"); + log::info!("🪁 reloading rsx"); for (old, new) in changed.into_iter() { let hr = get_location( &path.to_path_buf(), @@ -269,7 +269,7 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { .unwrap_or_else(|| vec![PathBuf::from("src")]); let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { - log::info!("reload required"); + // log::info!("🚧 reload required"); if chrono::Local::now().timestamp() > last_update_time { match build_manager.build() { Ok(_) => last_update_time = chrono::Local::now().timestamp(), From baab2b9b4f56f120735fd602d2cf923a6988e2f7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 6 Jul 2022 00:45:22 -0400 Subject: [PATCH 155/516] feat: use local bundle strategy --- .gitignore | 3 +- Cargo.toml | 6 +- build.rs | 50 + extension/dist/.gitignore | 2 + extension/package-lock.json | 4436 +++++++++++++++++++++++++---------- extension/package.json | 42 +- extension/server/.gitignore | 3 + extension/src/extension.ts | 139 -- extension/src/main.ts | 184 ++ src/cli/autoformat/mod.rs | 4 +- src/cli/mod.rs | 5 + src/cli/version.rs | 76 + src/main.rs | 5 + 13 files changed, 3604 insertions(+), 1351 deletions(-) create mode 100644 build.rs create mode 100644 extension/dist/.gitignore create mode 100644 extension/server/.gitignore delete mode 100644 extension/src/extension.ts create mode 100644 extension/src/main.ts create mode 100644 src/cli/version.rs diff --git a/.gitignore b/.gitignore index 2e049011e..5d6f4db1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target Cargo.lock -.idea/ \ No newline at end of file +.idea/ +.DS_Store diff --git a/Cargo.toml b/Cargo.toml index 9f85a89c2..506f14543 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,10 +46,8 @@ flate2 = "1.0.22" tar = "0.4.38" tower = "0.4.12" -dioxus-rsx = { path = "../../dioxus/packages/rsx" } -dioxus-autofmt = { path = "../../dioxus/packages/autofmt" } -# dioxus-rsx = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } -# dioxus-autofmt = { git = "https://github.com/dioxuslabs/dioxus.git", branch = "jk/rsx-refactor" } +dioxus-rsx = { git = "https://github.com/dioxuslabs/dioxus.git" } +dioxus-autofmt = { git = "https://github.com/dioxuslabs/dioxus.git" } [[bin]] path = "src/main.rs" diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..15935e2da --- /dev/null +++ b/build.rs @@ -0,0 +1,50 @@ +//! Construct version in the `commit-hash date channel` format + +use std::{env, path::PathBuf, process::Command}; + +fn main() { + set_rerun(); + set_commit_info(); + if option_env!("CFG_RELEASE").is_none() { + println!("cargo:rustc-env=POKE_RA_DEVS=1"); + } +} + +fn set_rerun() { + println!("cargo:rerun-if-env-changed=CFG_RELEASE"); + + let mut manifest_dir = PathBuf::from( + env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."), + ); + + while manifest_dir.parent().is_some() { + let head_ref = manifest_dir.join(".git/HEAD"); + if head_ref.exists() { + println!("cargo:rerun-if-changed={}", head_ref.display()); + return; + } + + manifest_dir.pop(); + } + + println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!"); +} + +fn set_commit_info() { + let output = match Command::new("git") + .arg("log") + .arg("-1") + .arg("--date=short") + .arg("--format=%H %h %cd") + .output() + { + Ok(output) if output.status.success() => output, + _ => return, + }; + let stdout = String::from_utf8(output.stdout).unwrap(); + let mut parts = stdout.split_whitespace(); + let mut next = || parts.next().unwrap(); + println!("cargo:rustc-env=RA_COMMIT_HASH={}", next()); + println!("cargo:rustc-env=RA_COMMIT_SHORT_HASH={}", next()); + println!("cargo:rustc-env=RA_COMMIT_DATE={}", next()) +} diff --git a/extension/dist/.gitignore b/extension/dist/.gitignore new file mode 100644 index 000000000..1287e9bd7 --- /dev/null +++ b/extension/dist/.gitignore @@ -0,0 +1,2 @@ +** +!.gitignore diff --git a/extension/package-lock.json b/extension/package-lock.json index 5de12fb6d..c671ca72c 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,92 +1,82 @@ { - "name": "dioxusstudio", + "name": "dioxus", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "dioxusstudio", + "name": "dioxus", "version": "0.0.1", "license": "MIT", + "dependencies": { + "vsce": "^2.9.2" + }, "devDependencies": { - "@types/node": "^12.12.0", - "@types/vscode": "^1.32.0", - "@typescript-eslint/eslint-plugin": "^4.16.0", - "@typescript-eslint/parser": "^4.16.0", - "eslint": "^7.21.0", - "typescript": "^4.3.5" + "@types/node": "^18.0.2", + "@types/vscode": "^1.68.1", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", + "cross-env": "^7.0.3", + "esbuild": "^0.14.27", + "eslint": "^8.19.0", + "eslint-config-prettier": "^8.5.0", + "ovsx": "^0.5.1", + "prettier": "^2.6.2", + "tslib": "^2.3.0", + "typescript": "^4.7.4", + "vsce": "^2.7.0" }, "engines": { - "vscode": "^1.32.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "node_modules/@babel/highlight": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.8.tgz", - "integrity": "sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "vscode": "^1.68.1" } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.4", + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { @@ -94,21 +84,21 @@ } }, "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { @@ -116,48 +106,49 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "node_modules/@types/node": { - "version": "12.12.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", - "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==", + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.2.tgz", + "integrity": "sha512-b947SdS4GH+g2W33wf5FzUu1KLj5FcSIiNWbU1ZyMvt/X7w48ZsVcsQoirIgE/Oq03WT5Qbn/dkY0hePi4ZXcQ==", "dev": true }, "node_modules/@types/vscode": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.33.0.tgz", - "integrity": "sha512-JSmGiValbrcG5g20jjCfKakLiuWyrcjVezj+SEAEZ4klXQktE5EtowuGlkLVqbkiBK4iY5wy/4yW8OjecuHnjQ==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.68.1.tgz", + "integrity": "sha512-fXlaq13NT5yHh6yZ3c+UxXloTSk34mIvsNFYyQCeO5Po2BLFAwz7EZT4kQ43B64/aPcnAenyWy3QasrTofBOnQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.16.1.tgz", - "integrity": "sha512-SK777klBdlkUZpZLC1mPvyOWk9yAFCWmug13eAjVQ4/Q1LATE/NbcQL1xDHkptQkZOLnPmLUA1Y54m8dqYwnoQ==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", + "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.16.1", - "@typescript-eslint/scope-manager": "4.16.1", - "debug": "^4.1.1", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/type-utils": "5.30.5", + "@typescript-eslint/utils": "5.30.5", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -165,50 +156,26 @@ } } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.16.1.tgz", - "integrity": "sha512-0Hm3LSlMYFK17jO4iY3un1Ve9x1zLNn4EM50Lia+0EV99NdbK+cn0er7HC7IvBA23mBg3P+8dUkMXy4leL33UQ==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.16.1", - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/typescript-estree": "4.16.1", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.16.1.tgz", - "integrity": "sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", + "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.16.1", - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/typescript-estree": "4.16.1", - "debug": "^4.1.1" + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -217,29 +184,55 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz", - "integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", + "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/visitor-keys": "4.16.1" + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", + "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.30.5", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/types": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz", - "integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", + "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -247,21 +240,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz", - "integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", + "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/visitor-keys": "4.16.1", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -273,36 +266,51 @@ } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz", - "integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==", + "node_modules/@typescript-eslint/utils": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", + "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.16.1", - "eslint-visitor-keys": "^2.0.0" + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", + "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.30.5", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -312,9 +320,9 @@ } }, "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -336,44 +344,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-union": { "version": "2.1.0", @@ -384,19 +383,57 @@ "node": ">=8" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/azure-devops-node-api": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.1.1.tgz", + "integrity": "sha512-XDG91XzLZ15reP12s3jFkKS8oiagSICjnLwxEYieme4+4h3ZveFOFRA4iYIG40RyHXsiI0mefFYYMFIJbMpWcg==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" } }, "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, "node_modules/brace-expansion": { @@ -421,6 +458,52 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -431,9 +514,9 @@ } }, "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -446,22 +529,57 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">= 6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "node_modules/chalk/node_modules/color-convert": { + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -473,54 +591,45 @@ "node": ">=7.0.0" } }, - "node_modules/chalk/node_modules/color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -535,22 +644,90 @@ "node": ">= 8" } }, - "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -575,74 +752,488 @@ "node": ">=6.0.0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=8.6" + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz", + "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-64": "0.14.48", + "esbuild-android-arm64": "0.14.48", + "esbuild-darwin-64": "0.14.48", + "esbuild-darwin-arm64": "0.14.48", + "esbuild-freebsd-64": "0.14.48", + "esbuild-freebsd-arm64": "0.14.48", + "esbuild-linux-32": "0.14.48", + "esbuild-linux-64": "0.14.48", + "esbuild-linux-arm": "0.14.48", + "esbuild-linux-arm64": "0.14.48", + "esbuild-linux-mips64le": "0.14.48", + "esbuild-linux-ppc64le": "0.14.48", + "esbuild-linux-riscv64": "0.14.48", + "esbuild-linux-s390x": "0.14.48", + "esbuild-netbsd-64": "0.14.48", + "esbuild-openbsd-64": "0.14.48", + "esbuild-sunos-64": "0.14.48", + "esbuild-windows-32": "0.14.48", + "esbuild-windows-64": "0.14.48", + "esbuild-windows-arm64": "0.14.48" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz", + "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz", + "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz", + "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz", + "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz", + "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz", + "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz", + "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz", + "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz", + "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz", + "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz", + "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz", + "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz", + "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz", + "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz", + "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz", + "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz", + "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz", + "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz", + "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz", + "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", - "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", + "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", "dev": true, "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -650,47 +1241,25 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", @@ -703,94 +1272,76 @@ "node": ">=8.0.0" } }, - "node_modules/eslint/node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, - "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, "engines": { "node": ">=10" } }, - "node_modules/eslint/node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "dependencies": { + "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": ">=4.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint/node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/esquery": { @@ -806,26 +1357,35 @@ } }, "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -844,6 +1404,15 @@ "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -851,20 +1420,31 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/fast-json-stable-stringify": { @@ -876,18 +1456,27 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -926,33 +1515,85 @@ } }, "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "dev": true }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -964,24 +1605,24 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -991,16 +1632,16 @@ } }, "node_modules/globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -1010,28 +1651,94 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, "engines": { - "node": ">= 4" + "node": ">= 0.4.0" } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -1056,7 +1763,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -1065,7 +1772,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -1078,28 +1785,37 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -1120,23 +1836,16 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -1151,9 +1860,29 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1167,10 +1896,19 @@ "node": ">= 0.8.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/lru-cache": { @@ -1185,6 +1923,37 @@ "node": ">=10" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1195,22 +1964,46 @@ } }, "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -1219,22 +2012,85 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-abi": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -1257,6 +2113,26 @@ "node": ">= 0.8.0" } }, + "node_modules/ovsx": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz", + "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==", + "dev": true, + "dependencies": { + "commander": "^6.1.0", + "follow-redirects": "^1.14.6", + "is-ci": "^2.0.0", + "leven": "^3.1.0", + "tmp": "^0.2.1", + "vsce": "^2.6.3" + }, + "bin": { + "ovsx": "lib/ovsx" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1269,10 +2145,53 @@ "node": ">=6" } }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "dependencies": { + "entities": "^4.3.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1296,10 +2215,16 @@ "node": ">=8" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -1308,6 +2233,32 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1317,13 +2268,29 @@ "node": ">= 0.8.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, "engines": { - "node": ">=0.4.0" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "node_modules/punycode": { @@ -1335,10 +2302,25 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", - "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -1355,10 +2337,60 @@ } ] }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { "node": ">=8" @@ -1367,15 +2399,6 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1433,10 +2456,36 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1469,6 +2518,65 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -1478,83 +2586,22 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "safe-buffer": "~5.2.0" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -1573,60 +2620,63 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "dependencies": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=6" } }, - "node_modules/table/node_modules/ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1640,15 +2690,15 @@ } }, "node_modules/tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/tsutils": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz", - "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "dependencies": { "tslib": "^1.8.1" @@ -1660,6 +2710,33 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1673,18 +2750,32 @@ } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" } }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1694,6 +2785,18 @@ "node": ">=4.2.0" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", + "dev": true + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1703,12 +2806,138 @@ "punycode": "^2.1.0" } }, - "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/vsce": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.9.2.tgz", + "integrity": "sha512-xyLqL4U82BilUX1t6Ym2opQEa2tLGWYjbgB7+ETeNVXlIJz5sWBJjQJSYJVFOKJSpiOtQclolu88cj7oY6vvPQ==", + "dev": true, + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "keytar": "^7.7.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.4.23", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/vsce/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/vsce/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/vsce/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/vsce/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/vsce/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1736,218 +2965,241 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } } }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.8.tgz", - "integrity": "sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, - "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/node": { - "version": "12.12.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", - "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==", + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.2.tgz", + "integrity": "sha512-b947SdS4GH+g2W33wf5FzUu1KLj5FcSIiNWbU1ZyMvt/X7w48ZsVcsQoirIgE/Oq03WT5Qbn/dkY0hePi4ZXcQ==", "dev": true }, "@types/vscode": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.33.0.tgz", - "integrity": "sha512-JSmGiValbrcG5g20jjCfKakLiuWyrcjVezj+SEAEZ4klXQktE5EtowuGlkLVqbkiBK4iY5wy/4yW8OjecuHnjQ==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.68.1.tgz", + "integrity": "sha512-fXlaq13NT5yHh6yZ3c+UxXloTSk34mIvsNFYyQCeO5Po2BLFAwz7EZT4kQ43B64/aPcnAenyWy3QasrTofBOnQ==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.16.1.tgz", - "integrity": "sha512-SK777klBdlkUZpZLC1mPvyOWk9yAFCWmug13eAjVQ4/Q1LATE/NbcQL1xDHkptQkZOLnPmLUA1Y54m8dqYwnoQ==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", + "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.16.1", - "@typescript-eslint/scope-manager": "4.16.1", - "debug": "^4.1.1", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/type-utils": "5.30.5", + "@typescript-eslint/utils": "5.30.5", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.16.1.tgz", - "integrity": "sha512-0Hm3LSlMYFK17jO4iY3un1Ve9x1zLNn4EM50Lia+0EV99NdbK+cn0er7HC7IvBA23mBg3P+8dUkMXy4leL33UQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.16.1", - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/typescript-estree": "4.16.1", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.16.1.tgz", - "integrity": "sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", + "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.16.1", - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/typescript-estree": "4.16.1", - "debug": "^4.1.1" + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", + "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz", - "integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", + "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/visitor-keys": "4.16.1" + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", + "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.30.5", + "debug": "^4.3.4", + "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz", - "integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", + "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz", - "integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", + "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.16.1", - "@typescript-eslint/visitor-keys": "4.16.1", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", + "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz", - "integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==", + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", + "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.16.1", - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - } + "@typescript-eslint/types": "5.30.5", + "eslint-visitor-keys": "^3.3.0" } }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -1963,35 +3215,26 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "array-union": { "version": "2.1.0", @@ -1999,16 +3242,43 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "azure-devops-node-api": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.1.1.tgz", + "integrity": "sha512-XDG91XzLZ15reP12s3jFkKS8oiagSICjnLwxEYieme4+4h3ZveFOFRA4iYIG40RyHXsiI0mefFYYMFIJbMpWcg==", + "dev": true, + "requires": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } }, "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "boolbase": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, "brace-expansion": { @@ -2030,6 +3300,32 @@ "fill-range": "^7.0.1" } }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2037,77 +3333,92 @@ "dev": true }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", "dev": true, "requires": { - "color-name": "1.1.3" + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2119,19 +3430,59 @@ "which": "^2.0.1" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, "requires": { - "ms": "^2.1.1" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" } }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", "dev": true }, "dir-glob": { @@ -2152,174 +3503,344 @@ "esutils": "^2.0.2" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" } }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true + }, + "esbuild": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz", + "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==", + "dev": true, + "requires": { + "esbuild-android-64": "0.14.48", + "esbuild-android-arm64": "0.14.48", + "esbuild-darwin-64": "0.14.48", + "esbuild-darwin-arm64": "0.14.48", + "esbuild-freebsd-64": "0.14.48", + "esbuild-freebsd-arm64": "0.14.48", + "esbuild-linux-32": "0.14.48", + "esbuild-linux-64": "0.14.48", + "esbuild-linux-arm": "0.14.48", + "esbuild-linux-arm64": "0.14.48", + "esbuild-linux-mips64le": "0.14.48", + "esbuild-linux-ppc64le": "0.14.48", + "esbuild-linux-riscv64": "0.14.48", + "esbuild-linux-s390x": "0.14.48", + "esbuild-netbsd-64": "0.14.48", + "esbuild-openbsd-64": "0.14.48", + "esbuild-sunos-64": "0.14.48", + "esbuild-windows-32": "0.14.48", + "esbuild-windows-64": "0.14.48", + "esbuild-windows-arm64": "0.14.48" + } + }, + "esbuild-android-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz", + "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz", + "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz", + "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz", + "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz", + "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz", + "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz", + "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz", + "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz", + "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz", + "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz", + "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz", + "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz", + "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz", + "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz", + "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz", + "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz", + "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz", + "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz", + "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz", + "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==", + "dev": true, + "optional": true + }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "eslint": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", - "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", + "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", "dev": true, "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true } } }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "esrecurse": "^4.1.0", + "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "eslint-visitor-keys": "^2.0.0" }, "dependencies": { "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true } } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, + "espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "dev": true, + "requires": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -2330,20 +3851,28 @@ }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } } }, "estraverse": { @@ -2358,6 +3887,12 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2365,17 +3900,27 @@ "dev": true }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "fast-json-stable-stringify": { @@ -2387,18 +3932,27 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2428,87 +3982,156 @@ } }, "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "dev": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" } }, "globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-fresh": { @@ -2524,13 +4147,13 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -2543,22 +4166,31 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -2573,23 +4205,16 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "json-schema-traverse": { @@ -2601,7 +4226,23 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "requires": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, "levn": { @@ -2614,10 +4255,19 @@ "type-check": "~0.4.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lru-cache": { @@ -2629,6 +4279,33 @@ "yallist": "^4.0.0" } }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true + } + } + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2636,40 +4313,106 @@ "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node-abi": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + }, + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "dev": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -2689,6 +4432,20 @@ "word-wrap": "^1.2.3" } }, + "ovsx": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz", + "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==", + "dev": true, + "requires": { + "commander": "^6.1.0", + "follow-redirects": "^1.14.6", + "is-ci": "^2.0.0", + "leven": "^3.1.0", + "tmp": "^0.2.1", + "vsce": "^2.6.3" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2698,10 +4455,46 @@ "callsites": "^3.0.0" } }, + "parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "requires": { + "semver": "^5.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "requires": { + "entities": "^4.3.0" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { @@ -2716,46 +4509,125 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, "queue-microtask": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", - "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + } + } + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "resolve-from": { @@ -2788,10 +4660,22 @@ "queue-microtask": "^1.2.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -2812,73 +4696,56 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "safe-buffer": "~5.2.0" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { @@ -2888,52 +4755,54 @@ "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, - "table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "requires": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" } }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2944,18 +4813,41 @@ } }, "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tsutils": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz", - "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" } }, "type-check": { @@ -2968,15 +4860,38 @@ } }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "requires": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", "dev": true }, "uri-js": { @@ -2988,12 +4903,116 @@ "punycode": "^2.1.0" } }, - "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "vsce": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.9.2.tgz", + "integrity": "sha512-xyLqL4U82BilUX1t6Ym2opQEa2tLGWYjbgB7+ETeNVXlIJz5sWBJjQJSYJVFOKJSpiOtQclolu88cj7oY6vvPQ==", + "dev": true, + "requires": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "keytar": "^7.7.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.4.23", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3012,7 +5031,23 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true }, "yallist": { @@ -3020,6 +5055,25 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3" + } } } } diff --git a/extension/package.json b/extension/package.json index f884637c8..3e4ca4b83 100644 --- a/extension/package.json +++ b/extension/package.json @@ -12,10 +12,10 @@ "url": "https://github.com/DioxusLabs/cli" }, "engines": { - "vscode": "^1.32.0" + "vscode": "^1.68.1" }, "categories": [ - "Other" + "Programming Languages" ], "activationEvents": [ "onCommand:extension.htmlToDioxusRsx", @@ -23,7 +23,7 @@ "onCommand:extension.formatRsx", "onCommand:extension.formatRsxDocument" ], - "main": "./out/extension", + "main": "./out/main", "contributes": { "commands": [ { @@ -45,18 +45,32 @@ ] }, "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc -p ./", - "lint": "eslint . --ext .ts,.tsx", - "watch": "tsc -watch -p ./", - "package": "vsce package" + "vscode:prepublish": "npm run build-base -- --minify", + "package": "vsce package -o rust-analyzer.vsix", + "build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16", + "build": "npm run build-base -- --sourcemap", + "watch": "npm run build-base -- --sourcemap --watch", + "lint": "prettier --check . && eslint -c .eslintrc.js --ext ts ./src ./tests", + "fix": "prettier --write . && eslint -c .eslintrc.js --ext ts ./src ./tests --fix", + "pretest": "tsc && npm run build", + "test": "cross-env TEST_VARIABLE=test node ./out/tests/runTests.js" }, "devDependencies": { - "@types/node": "^12.12.0", - "@types/vscode": "^1.32.0", - "@typescript-eslint/eslint-plugin": "^4.16.0", - "@typescript-eslint/parser": "^4.16.0", - "eslint": "^7.21.0", - "typescript": "^4.3.5" + "@types/node": "^18.0.2", + "@types/vscode": "^1.68.1", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", + "cross-env": "^7.0.3", + "esbuild": "^0.14.27", + "eslint": "^8.19.0", + "typescript": "^4.7.4", + "eslint-config-prettier": "^8.5.0", + "ovsx": "^0.5.1", + "prettier": "^2.6.2", + "tslib": "^2.3.0", + "vsce": "^2.7.0" + }, + "dependencies": { + "vsce": "^2.9.2" } } diff --git a/extension/server/.gitignore b/extension/server/.gitignore new file mode 100644 index 000000000..f42761bea --- /dev/null +++ b/extension/server/.gitignore @@ -0,0 +1,3 @@ +** +!Readme.md +!.gitignore diff --git a/extension/src/extension.ts b/extension/src/extension.ts deleted file mode 100644 index 44d5b8f16..000000000 --- a/extension/src/extension.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as vscode from 'vscode'; -import { spawn } from "child_process"; - -export function activate(context: vscode.ExtensionContext) { - - - function translate(component: boolean) { - const editor = vscode.window.activeTextEditor;// Get the active text editor - if (!editor) return; - - const html = editor.document.getText(editor.selection); - if (html.length == 0) { - vscode.window.showWarningMessage("Please select HTML fragment before invoking this command!"); - return; - } - - let params = ["translate"]; - if (component) params.push("--component"); - params.push("--raw"); - params.push(html); - - const child_proc = spawn("dioxus", params); - - let result = ''; - child_proc.stdout?.on('data', data => result += data); - - child_proc.on('close', () => { - if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); - }); - - child_proc.on('error', (err) => { - vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); - }); - } - - function autoformat_selection() { - const editor = vscode.window.activeTextEditor;// Get the active text editor - if (!editor) return; - - const unformatted = editor.document.getText(editor.selection); - if (unformatted.length == 0) { - vscode.window.showWarningMessage("Please select rsx invoking this command!"); - return; - } - - let args = ["fmt", "--raw", unformatted.toString()]; - const child_proc = spawn("dioxus", args); - let result = ''; - - child_proc.stdout?.on('data', data => result += data); - - child_proc.on('close', () => { - if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); - }); - - child_proc.on('error', (err) => { - vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); - }); - } - - - function autoformat_document(document: vscode.TextDocument) { - // check the settings to make sure format on save is configured - // const formatOnSave: string | undefined = vscode.workspace.getConfiguration('rust').get('formatOnSave'); - - if (document.languageId === "rust" && document.uri.scheme === "file") { - const active_editor = vscode.window.activeTextEditor;// Get the active text editor - - if (active_editor?.document.fileName === document.fileName) { - const text = document.getText(); - - console.error(text); - - const child_proc = spawn("dioxus", ["fmt", "-f", text]); - - let result = ''; - - child_proc.stdout?.on('data', data => result += data); - - type RsxEdit = { - formatted: string, - start: number, - end: number - } - - child_proc.on('close', () => { - - if (result.length > 0) { - let decoded: RsxEdit[] = JSON.parse(result); - - console.log("Decoded edits: ", decoded); - - // Get the active text editor - const cur_editor = vscode.window.activeTextEditor; - - - // even if we build an empty edit stream, it tries to save the editor - // this makes sure the editor is actually saved - if (decoded.length > 0 && cur_editor == active_editor) { - active_editor.edit(editBuilder => { - - for (let edit of decoded) { - console.log("Handling Edit: ", edit); - - let start = document.positionAt(edit.start - 1); - let end = document.positionAt(edit.end + 1); - const range = new vscode.Range(start, end); - - editBuilder.replace(range, `{ ${edit.formatted} }`); - // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { - // }); - // editor.edit(editBuilder => editBuilder.replace(range, `{ ${edit.formatted} }`)).then((could_be_applied) => { - // console.log("Edit applied: ", could_be_applied); - // }); - } - }) - } - } - }); - - child_proc.on('error', (err) => { - vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); - }); - } - } - } - - context.subscriptions.push( - vscode.commands.registerCommand('extension.htmlToDioxusRsx', () => translate(false)), - vscode.commands.registerCommand('extension.htmlToDioxusComponent', () => translate(true)), - vscode.commands.registerCommand('extension.formatRsx', () => autoformat_selection()), - vscode.commands.registerCommand('extension.formatRsxDocument', () => { - const editor = vscode.window.activeTextEditor;// Get the active text editor - if (!editor) return; - autoformat_document(editor.document); - }), - vscode.workspace.onDidSaveTextDocument(autoformat_document) - ); -} diff --git a/extension/src/main.ts b/extension/src/main.ts new file mode 100644 index 000000000..156d45565 --- /dev/null +++ b/extension/src/main.ts @@ -0,0 +1,184 @@ +import * as vscode from 'vscode'; +import { spawn } from "child_process"; + +let serverPath: string = "dioxus"; + +export async function activate(context: vscode.ExtensionContext) { + let somePath = await bootstrap(context); + + if (somePath == undefined) { + await vscode.window.showErrorMessage('Could not find bundled Dioxus-CLI. Please install it manually.'); + return; + } else { + serverPath = somePath; + } + + context.subscriptions.push( + vscode.commands.registerTextEditorCommand('editor.action.clipboardPasteAction', onPasteHandler), + vscode.commands.registerCommand('extension.htmlToDioxusRsx', translateBlock), + vscode.commands.registerCommand('extension.htmlToDioxusComponent', translateComponent), + vscode.commands.registerCommand('extension.formatRsx', fmtSelection), + vscode.commands.registerCommand('extension.formatRsxDocument', formatRsxDocument), + vscode.workspace.onWillSaveTextDocument(fmtDocument) + ); +} + + +function translateComponent() { + translate(true) +} + +function translateBlock() { + translate(false) +} + +function translate(component: boolean) { + const editor = vscode.window.activeTextEditor; + + if (!editor) return; + + const html = editor.document.getText(editor.selection); + if (html.length == 0) { + vscode.window.showWarningMessage("Please select HTML fragment before invoking this command!"); + return; + } + + let params = ["translate"]; + if (component) params.push("--component"); + params.push("--raw", html); + + const child_proc = spawn(serverPath, params); + + let result = ''; + child_proc.stdout?.on('data', data => result += data); + + child_proc.on('close', () => { + if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); +} + +function onPasteHandler() { + // check settings to see if we should convert HTML to Rsx + if (vscode.workspace.getConfiguration('dioxus').get('convertOnPaste')) { + convertHtmlToRsxOnPaste(); + } +} + +function convertHtmlToRsxOnPaste() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + // get the cursor location + const cursor = editor.selection.active; + + // try to parse the HTML at the cursor location + const html = editor.document.getText(new vscode.Range(cursor, cursor)); +} + +function formatRsxDocument() { + const editor = vscode.window.activeTextEditor; + if (editor) { + fmtDocument(); + } +} + +function fmtSelection() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + + const unformatted = editor.document.getText(editor.selection); + + if (unformatted.length == 0) { + vscode.window.showWarningMessage("Please select rsx invoking this command!"); + return; + } + + const child_proc = spawn(serverPath, ["fmt", "--raw", unformatted.toString()]); + let result = ''; + + child_proc.stdout?.on('data', data => result += data); + + child_proc.on('close', () => { + if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); +} + +function fmtDocument() { + const editor = vscode.window.activeTextEditor; + const document = editor?.document; + if (!document) return; + + // check the settings to make sure format on save is configured + if (document.languageId === "rust" && document.uri.scheme === "file") { + const active_editor = vscode.window.activeTextEditor; + + if (active_editor?.document.fileName === document.fileName) { + const text = document.getText(); + const child_proc = spawn("dioxus", ["fmt", "-f", text]); + + let result = ''; + child_proc.stdout?.on('data', data => result += data); + + type RsxEdit = { + formatted: string, + start: number, + end: number + } + + child_proc.on('close', () => { + if (result.length > 0) { + let decoded: RsxEdit[] = JSON.parse(result); + + if (decoded.length > 0) { + active_editor.edit(editBuilder => { + decoded.map((edit) => { + editBuilder.replace(new vscode.Range( + document.positionAt(edit.start), + document.positionAt(edit.end) + ), edit.formatted); + }); + }, { + undoStopAfter: false, + undoStopBefore: false + }) + } + } + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); + } + } +} + + +// I'm using the approach defined in rust-analyzer here +// +// We ship the server as part of the extension, but we need to handle external paths and such +// +// https://github.com/rust-lang/rust-analyzer/blob/fee5555cfabed4b8abbd40983fc4442df4007e49/editors/code/src/main.ts#L270 +async function bootstrap(context: vscode.ExtensionContext): Promise { + + const ext = process.platform === "win32" ? ".exe" : ""; + const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `dioxus${ext}`); + const bundledExists = await vscode.workspace.fs.stat(bundled).then( + () => true, + () => false + ); + + // if bunddled doesn't exist, try using a locally-installed version + if (!bundledExists) { + return "dioxus"; + } + + return bundled.fsPath; +} diff --git a/src/cli/autoformat/mod.rs b/src/cli/autoformat/mod.rs index 3008411db..5c1532d58 100644 --- a/src/cli/autoformat/mod.rs +++ b/src/cli/autoformat/mod.rs @@ -15,7 +15,7 @@ pub struct Autoformat { impl Autoformat { pub fn autoformat(self) -> Result<()> { if let Some(raw) = self.raw { - if let Some(inner) = dioxus_autofmt::fmt_block(&raw) { + if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0) { println!("{}", inner); } else { // exit process with error @@ -25,7 +25,7 @@ impl Autoformat { } if let Some(file) = self.file { - let edits = dioxus_autofmt::get_format_blocks(&file); + let edits = dioxus_autofmt::fmt_file(&file); let as_json = serde_json::to_string(&edits).unwrap(); println!("{}", as_json); } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 92e05eaa9..efd72b3cd 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -7,6 +7,7 @@ pub mod create; pub mod serve; pub mod tool; pub mod translate; +pub mod version; use crate::{ cfg::{ConfigOptsBuild, ConfigOptsServe}, @@ -59,6 +60,10 @@ pub enum Commands { /// Clean output artifacts. Clean(clean::Clean), + /// Print the version of this extension + #[clap(name = "version")] + Version(version::Version), + /// Format some rsx #[clap(name = "fmt")] Autoformat(autoformat::Autoformat), diff --git a/src/cli/version.rs b/src/cli/version.rs new file mode 100644 index 000000000..3c77ba419 --- /dev/null +++ b/src/cli/version.rs @@ -0,0 +1,76 @@ +use super::*; + +/// Build the Rust WASM app and all of its assets. +#[derive(Clone, Debug, Parser)] +#[clap(name = "version")] +pub struct Version {} + +impl Version { + pub fn version(self) -> VersionInfo { + version() + } +} + +use std::fmt; + +/// Information about the git repository where rust-analyzer was built from. +pub struct CommitInfo { + pub short_commit_hash: &'static str, + pub commit_hash: &'static str, + pub commit_date: &'static str, +} + +/// Cargo's version. +pub struct VersionInfo { + /// rust-analyzer's version, such as "1.57.0", "1.58.0-beta.1", "1.59.0-nightly", etc. + pub version: &'static str, + + /// The release channel we were built for (stable/beta/nightly/dev). + /// + /// `None` if not built via rustbuild. + pub release_channel: Option<&'static str>, + + /// Information about the Git repository we may have been built from. + /// + /// `None` if not built from a git repo. + pub commit_info: Option, +} + +impl fmt::Display for VersionInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.version)?; + + if let Some(ci) = &self.commit_info { + write!(f, " ({} {})", ci.short_commit_hash, ci.commit_date)?; + }; + Ok(()) + } +} + +/// Returns information about cargo's version. +pub const fn version() -> VersionInfo { + let version = match option_env!("CFG_RELEASE") { + Some(x) => x, + None => "0.0.0", + }; + + let release_channel = option_env!("CFG_RELEASE_CHANNEL"); + let commit_info = match ( + option_env!("RA_COMMIT_SHORT_HASH"), + option_env!("RA_COMMIT_HASH"), + option_env!("RA_COMMIT_DATE"), + ) { + (Some(short_commit_hash), Some(commit_hash), Some(commit_date)) => Some(CommitInfo { + short_commit_hash, + commit_hash, + commit_date, + }), + _ => None, + }; + + VersionInfo { + version, + release_channel, + commit_info, + } +} diff --git a/src/main.rs b/src/main.rs index 72558741e..f72cb7666 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,6 +54,11 @@ async fn main() -> Result<()> { log::error!("format error: {}", e); } } + + Commands::Version(opt) => { + let version = opt.version(); + println!("{}", version); + } } Ok(()) From 719e48c0a5f36cad505d2b0b6dd4a2bc9b212a6e Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 16:16:16 +0800 Subject: [PATCH 156/516] feat: beautify console --- Cargo.toml | 3 ++- src/cli/mod.rs | 6 +----- src/lib.rs | 4 +++- src/server/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42de2ba4c..fa14b36b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-cli" -version = "0.1.4" +version = "0.1.5" authors = ["Jonathan Kelley"] edition = "2021" 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"] } thiserror = "1.0.30" wasm-bindgen-cli-support = "0.2.79" +colored = "2.0.0" # features log = "0.4.14" diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d639d28a7..b456d4705 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -27,7 +27,7 @@ use std::{ /// Build, bundle, & ship your Dioxus app. #[derive(Parser)] -#[clap(name = "dioxus")] +#[clap(name = "dioxus", version)] pub struct Cli { #[clap(subcommand)] pub action: Commands, @@ -35,10 +35,6 @@ pub struct Cli { /// Enable verbose logging. #[clap(short)] 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, } #[derive(Parser)] diff --git a/src/lib.rs b/src/lib.rs index 6d009c18c..9956989b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +pub const DIOXUS_CLI_VERSION: &'static str = "0.1.5"; + pub mod builder; pub mod server; pub mod tools; @@ -19,4 +21,4 @@ pub use error::*; pub mod logging; pub use logging::*; -pub mod hot_reload; +pub mod hot_reload; \ No newline at end of file diff --git a/src/server/mod.rs b/src/server/mod.rs index 446d9d75c..886088fd9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,11 +6,12 @@ use axum::{ routing::{get, get_service}, Router, }; +use colored::Colorize; use dioxus::rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; use syn::spanned::Spanned; -use std::{path::PathBuf, sync::Arc}; +use std::{path::PathBuf, process::Command, sync::Arc}; use tower::ServiceBuilder; use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; @@ -180,7 +181,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } // 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); let file_service_config = config.clone(); let file_service = ServiceBuilder::new() @@ -290,7 +291,7 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { } // 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); let file_service_config = config.clone(); let file_service = ServiceBuilder::new() @@ -348,6 +349,44 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { Ok(()) } +fn print_console_info(port: u16, config: &CrateConfig) { + print!( + "{}", + String::from_utf8_lossy( + &Command::new(if cfg!(target_os = "windows") { + "cls" + } else { + "clear" + }) + .output() + .unwrap() + .stdout + ) + ); + + 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 workspace = config.workspace_dir.to_str().unwrap().to_string(); + + println!( + "{} @ v{}\n", + "Dioxus".bold().green(), + crate::DIOXUS_CLI_VERSION + ); + println!( + "\t> Local: {}", + format!("https://localhost:{}/", port).blue() + ); + println!("\t> Profile: {}", profile.green()); + println!("\t> Hot Reload: {}", hot_reload.green()); + println!("\t> Workspace: {}", workspace.yellow()); + + println!("\n{}", "Server startup completed.".bold()); +} + async fn ws_handler( ws: WebSocketUpgrade, _: Option>, From b2c5141fa6f62b4eaf3e4918dc5b6e29c2209785 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 17:36:14 +0800 Subject: [PATCH 157/516] feat: commit code --- src/server/mod.rs | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 886088fd9..b1e527b86 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -369,22 +369,54 @@ fn print_console_info(port: u16, config: &CrateConfig) { profile = config.custom_profile.as_ref().unwrap().to_string(); } let hot_reload = if config.hot_reload { "RSX" } else { "Normal" }; - let workspace = config.workspace_dir.to_str().unwrap().to_string(); + let tools_str = format!( + "{:?}", + config + .dioxus_config + .application + .tools + .clone() + .unwrap_or_default() + .iter() + .map(|d| d.0.clone()) + .collect::>() + ); + 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" + }; println!( "{} @ v{}\n", "Dioxus".bold().green(), - crate::DIOXUS_CLI_VERSION + crate::DIOXUS_CLI_VERSION, ); println!( - "\t> Local: {}", + "\t> Local : {}", format!("https://localhost:{}/", port).blue() ); - println!("\t> Profile: {}", profile.green()); - println!("\t> Hot Reload: {}", hot_reload.green()); - println!("\t> Workspace: {}", workspace.yellow()); + println!("\t> NetWork : {}", "use --host to expose".white().dimmed()); + println!(""); + println!("\t> Profile : {}", profile.green()); + println!("\t> Hot Reload : {}", hot_reload.cyan()); + println!("\t> Enabled Tools : {}", tools_str.yellow()); + println!("\t> Index Template : {}", custom_html_file.green()); + println!("\t> URL Rewrite : {}", url_rewrite.purple()); - println!("\n{}", "Server startup completed.".bold()); + println!("\n{}", "Server startup completed.\n".bold()); } async fn ws_handler( From 9e64b5dde88609d43ac14cccc571b39318356c05 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 17:46:46 +0800 Subject: [PATCH 158/516] feat: commit code --- src/server/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index b1e527b86..87448cda9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -416,7 +416,7 @@ fn print_console_info(port: u16, config: &CrateConfig) { println!("\t> Index Template : {}", custom_html_file.green()); println!("\t> URL Rewrite : {}", url_rewrite.purple()); - println!("\n{}", "Server startup completed.\n".bold()); + println!("\n{}\n", "Server startup completed.".green().bold()); } async fn ws_handler( From 8dbf571891bad849eb54c57bddd3849f1c02e8bf Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 18:11:25 +0800 Subject: [PATCH 159/516] feat: commit console print --- src/server/mod.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 87448cda9..93e81ea33 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,7 +11,7 @@ use dioxus::rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; use syn::spanned::Spanned; -use std::{path::PathBuf, process::Command, sync::Arc}; +use std::{path::PathBuf, process::Command, sync::Arc, net::UdpSocket}; use tower::ServiceBuilder; use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; @@ -408,17 +408,41 @@ fn print_console_info(port: u16, config: &CrateConfig) { "\t> Local : {}", format!("https://localhost:{}/", port).blue() ); - println!("\t> NetWork : {}", "use --host to expose".white().dimmed()); + 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> Enabled Tools : {}", tools_str.yellow()); println!("\t> Index Template : {}", custom_html_file.green()); - println!("\t> URL Rewrite : {}", url_rewrite.purple()); + println!("\t> URL Rewrite [index_on_404] : {}", url_rewrite.purple()); println!("\n{}\n", "Server startup completed.".green().bold()); } +fn get_ip() -> Option { + 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( ws: WebSocketUpgrade, _: Option>, From 81b712ec0baab63186769cfa62b1962bfd90af24 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 21:08:46 +0800 Subject: [PATCH 160/516] feat: commit code --- src/server/mod.rs | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 93e81ea33..c5b438768 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,7 +11,7 @@ use dioxus::rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; use syn::spanned::Spanned; -use std::{path::PathBuf, process::Command, sync::Arc, net::UdpSocket}; +use std::{net::UdpSocket, path::PathBuf, process::Command, sync::Arc}; use tower::ServiceBuilder; use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; @@ -270,12 +270,17 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { .clone() .unwrap_or_else(|| vec![PathBuf::from("src")]); - let mut watcher = RecommendedWatcher::new(move |_: notify::Result| { - // log::info!("🚧 reload required"); - if chrono::Local::now().timestamp() > last_update_time { - match build_manager.build() { - Ok(_) => last_update_time = chrono::Local::now().timestamp(), - Err(e) => log::error!("{}", e), + let mut watcher = RecommendedWatcher::new(move |info: notify::Result| { + if info.is_ok() { + let info = info.unwrap(); + if chrono::Local::now().timestamp() > last_update_time { + match build_manager.build() { + Ok(_) => { + last_update_time = chrono::Local::now().timestamp(); + print_rebuild_info(info.paths); + } + Err(e) => log::error!("{}", e), + } } } }) @@ -414,7 +419,8 @@ fn print_console_info(port: u16, config: &CrateConfig) { "http://{}:{}/", get_ip().unwrap_or(String::from("0.0.0.0")), port - ).blue() + ) + .blue() ); println!(""); println!("\t> Profile : {}", profile.green()); @@ -426,6 +432,28 @@ fn print_console_info(port: u16, config: &CrateConfig) { println!("\n{}\n", "Server startup completed.".green().bold()); } +fn print_rebuild_info(paths: Vec) { + print!( + "{}", + String::from_utf8_lossy( + &Command::new(if cfg!(target_os = "windows") { + "cls" + } else { + "clear" + }) + .output() + .unwrap() + .stdout + ) + ); + + for path in paths { + let path = path.strip_prefix(crate::crate_root().unwrap()).unwrap().to_path_buf(); + log::info!("Updated {}", format!("{}", path.to_str().unwrap()).green()); + } + log::info!("Project rebuild done.") +} + fn get_ip() -> Option { let socket = match UdpSocket::bind("0.0.0.0:0") { Ok(s) => s, From 61c06ea5987ada72528d7625380c69a0cea5c5d2 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 21:23:35 +0800 Subject: [PATCH 161/516] feat: commit console log --- src/builder.rs | 6 +++++- src/cli/build/mod.rs | 2 +- src/cli/serve/mod.rs | 2 +- src/server/hot_reload.rs | 2 +- src/server/mod.rs | 36 ++++++++++++++++++------------------ 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 02add84ad..f69ce3637 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -13,7 +13,7 @@ use std::{ }; use wasm_bindgen_cli_support::Bindgen; -pub fn build(config: &CrateConfig) -> Result<()> { +pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { // [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 // [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm @@ -52,6 +52,10 @@ pub fn build(config: &CrateConfig) -> Result<()> { cmd.arg("--verbose"); } + if quiet { + cmd.arg("--quiet"); + } + if config.custom_profile.is_some() { let custom_profile = config.custom_profile.as_ref().unwrap(); cmd.arg("--profile"); diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs index 661f17a5a..f857bfc13 100644 --- a/src/cli/build/mod.rs +++ b/src/cli/build/mod.rs @@ -34,7 +34,7 @@ impl Build { match platform.as_str() { "web" => { - crate::builder::build(&crate_config)?; + crate::builder::build(&crate_config, false)?; } "desktop" => { crate::builder::build_desktop(&crate_config, false)?; diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 5528ebade..62843d653 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -40,7 +40,7 @@ impl Serve { match platform.as_str() { "web" => { - crate::builder::build(&crate_config)?; + crate::builder::build(&crate_config, false)?; } "desktop" => { crate::builder::build_desktop(&crate_config, true)?; diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 0338dacc5..3b7812834 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -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); }, Error::RecompileRequiredError(_) => { - if let Err(err) = state.build_manager.build(){ + if let Err(err) = state.build_manager.rebuild(){ log::error!("{}", err); } } diff --git a/src/server/mod.rs b/src/server/mod.rs index c5b438768..6b0b90a68 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -27,9 +27,9 @@ pub struct BuildManager { } impl BuildManager { - fn build(&self) -> Result<()> { - log::info!("🪁 Rebuild code"); - builder::build(&self.config)?; + fn rebuild(&self) -> Result<()> { + log::info!("🪁 Rebuild project"); + builder::build(&self.config, true)?; // change the websocket reload state to true; // the page will auto-reload. if self @@ -158,7 +158,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } if needs_rebuild { log::info!("reload required"); - if let Err(err) = build_manager.build() { + if let Err(err) = build_manager.rebuild() { log::error!("{}", err); } } @@ -274,7 +274,7 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { if info.is_ok() { let info = info.unwrap(); if chrono::Local::now().timestamp() > last_update_time { - match build_manager.build() { + match build_manager.rebuild() { Ok(_) => { last_update_time = chrono::Local::now().timestamp(); print_rebuild_info(info.paths); @@ -433,19 +433,19 @@ fn print_console_info(port: u16, config: &CrateConfig) { } fn print_rebuild_info(paths: Vec) { - print!( - "{}", - String::from_utf8_lossy( - &Command::new(if cfg!(target_os = "windows") { - "cls" - } else { - "clear" - }) - .output() - .unwrap() - .stdout - ) - ); + // print!( + // "{}", + // String::from_utf8_lossy( + // &Command::new(if cfg!(target_os = "windows") { + // "cls" + // } else { + // "clear" + // }) + // .output() + // .unwrap() + // .stdout + // ) + // ); for path in paths { let path = path.strip_prefix(crate::crate_root().unwrap()).unwrap().to_path_buf(); From 14a2ec306eb64e1ad8a5a293c0225dbfd1d29487 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 22:37:27 +0800 Subject: [PATCH 162/516] feat: commit code --- src/server/mod.rs | 62 +++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 6b0b90a68..ba9a853f6 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -181,7 +181,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0:8080 - print_console_info(port, &config); + print_console_info(port, &config, vec![]); let file_service_config = config.clone(); let file_service = ServiceBuilder::new() @@ -270,15 +270,14 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { .clone() .unwrap_or_else(|| vec![PathBuf::from("src")]); + let watcher_config = config.clone(); let mut watcher = RecommendedWatcher::new(move |info: notify::Result| { + let config = watcher_config.clone(); if info.is_ok() { - let info = info.unwrap(); + print_console_info(port, &config, info.unwrap().paths); if chrono::Local::now().timestamp() > last_update_time { match build_manager.rebuild() { - Ok(_) => { - last_update_time = chrono::Local::now().timestamp(); - print_rebuild_info(info.paths); - } + Ok(_) => last_update_time = chrono::Local::now().timestamp(), Err(e) => log::error!("{}", e), } } @@ -296,7 +295,7 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0 - print_console_info(port, &config); + print_console_info(port, &config, vec![]); let file_service_config = config.clone(); let file_service = ServiceBuilder::new() @@ -354,7 +353,7 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { Ok(()) } -fn print_console_info(port: u16, config: &CrateConfig) { +fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { print!( "{}", String::from_utf8_lossy( @@ -369,6 +368,14 @@ fn print_console_info(port: u16, config: &CrateConfig) { ) ); + // 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(); @@ -404,11 +411,18 @@ fn print_console_info(port: u16, config: &CrateConfig) { "False" }; - println!( - "{} @ v{}\n", - "Dioxus".bold().green(), - crate::DIOXUS_CLI_VERSION, - ); + if changed.len() <= 0 { + println!( + "{} @ v{}\n", + "Dioxus".bold().green(), + crate::DIOXUS_CLI_VERSION, + ); + } else { + println!( + "Project Reloaded: {}\n", + format!("Changed {} files.", changed.len()).purple().bold() + ); + } println!( "\t> Local : {}", format!("https://localhost:{}/", port).blue() @@ -432,28 +446,6 @@ fn print_console_info(port: u16, config: &CrateConfig) { println!("\n{}\n", "Server startup completed.".green().bold()); } -fn print_rebuild_info(paths: Vec) { - // print!( - // "{}", - // String::from_utf8_lossy( - // &Command::new(if cfg!(target_os = "windows") { - // "cls" - // } else { - // "clear" - // }) - // .output() - // .unwrap() - // .stdout - // ) - // ); - - for path in paths { - let path = path.strip_prefix(crate::crate_root().unwrap()).unwrap().to_path_buf(); - log::info!("Updated {}", format!("{}", path.to_str().unwrap()).green()); - } - log::info!("Project rebuild done.") -} - fn get_ip() -> Option { let socket = match UdpSocket::bind("0.0.0.0:0") { Ok(s) => s, From 3551d1b6a0aeeebc3d52e5399f4f0e70703df325 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 22:44:50 +0800 Subject: [PATCH 163/516] feat: commit code --- src/server/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index ba9a853f6..d6efab958 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -94,14 +94,16 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { .clone() .unwrap_or_else(|| vec![PathBuf::from("src")]); + let watcher_config = config.clone(); let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { + let config = watcher_config.clone(); if chrono::Local::now().timestamp() > last_update_time { // Give time for the change to take effect before reading the file std::thread::sleep(std::time::Duration::from_millis(100)); if let Ok(evt) = evt { let mut messages = Vec::new(); let mut needs_rebuild = false; - for path in evt.paths { + for path in evt.paths.clone() { let mut file = File::open(path.clone()).unwrap(); if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { continue; @@ -157,7 +159,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } } if needs_rebuild { - log::info!("reload required"); + print_console_info(port, &config, evt.paths); if let Err(err) = build_manager.rebuild() { log::error!("{}", err); } From b642dcd739a0403837ca43cd661d8d24ac22012d Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 22:45:08 +0800 Subject: [PATCH 164/516] fix: format code --- src/builder.rs | 4 +++- src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index f69ce3637..4feb87839 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -433,7 +433,9 @@ fn build_assets(config: &CrateConfig) -> Result> { let temp = entry.path(); if temp.is_file() { let suffix = temp.extension(); - if suffix.is_none() { continue; } + 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. diff --git a/src/lib.rs b/src/lib.rs index 9956989b7..1b651acee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,4 +21,4 @@ pub use error::*; pub mod logging; pub use logging::*; -pub mod hot_reload; \ No newline at end of file +pub mod hot_reload; From f57dc5212b37bcc799feded01468c0251f60822a Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Wed, 6 Jul 2022 23:01:39 +0800 Subject: [PATCH 165/516] fix: change local `https` to `http` --- src/server/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index d6efab958..3ecce6c26 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -427,7 +427,7 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { } println!( "\t> Local : {}", - format!("https://localhost:{}/", port).blue() + format!("http://localhost:{}/", port).blue() ); println!( "\t> NetWork : {}", @@ -444,8 +444,14 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { println!("\t> Enabled Tools : {}", tools_str.yellow()); println!("\t> Index Template : {}", custom_html_file.green()); println!("\t> URL Rewrite [index_on_404] : {}", url_rewrite.purple()); + println!(""); - println!("\n{}\n", "Server startup completed.".green().bold()); + if changed.len() <= 0 { + log::info!("{}", "Server startup completed.".green().bold()); + } else { + log::info!("{}", "Project rebuild completed.".green().bold()); + } + } fn get_ip() -> Option { From 7860f8b931ecf7d5757433f53868561929d95e74 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Thu, 7 Jul 2022 23:03:30 +0800 Subject: [PATCH 166/516] feat: commit code --- Cargo.toml | 1 + src/builder.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fa14b36b7..7e982bc78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ regex = "1.5.4" chrono = "0.4.19" anyhow = "1.0.53" hyper = "0.14.17" +loading = "0.3.0" axum = { version = "0.5.1", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/builder.rs b/src/builder.rs index 4feb87839..0dce6d71d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -6,7 +6,7 @@ use crate::{ }; use std::{ fs::{copy, create_dir_all, remove_dir_all, File}, - io::Read, + io::{Read, BufRead}, panic, path::PathBuf, process::Command, @@ -42,8 +42,9 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { .arg("build") .arg("--target") .arg("wasm32-unknown-unknown") - .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()); + .arg("--message-format=json") + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()); if config.release { cmd.arg("--release"); @@ -74,7 +75,14 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { ExecutableType::Example(name) => cmd.arg("--example").arg(name), }; + let loading = loading::Loading::default(); + loading.text("Building"); let output = cmd.output()?; + for i in output.stdout.lines() { + // println!("{:?}", i); + } + loading.success("OK"); + loading.end(); if !output.status.success() { log::error!("Build failed!"); From 5edcb9238c33c253ca62831bbd8947d4e8b49c05 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Fri, 8 Jul 2022 09:26:05 +0800 Subject: [PATCH 167/516] feat: cargo format struct --- src/builder.rs | 26 ++++++++++++++++++-------- src/cargo.rs | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 0dce6d71d..a662f8a20 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -77,18 +77,28 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { let loading = loading::Loading::default(); loading.text("Building"); - let output = cmd.output()?; - for i in output.stdout.lines() { - // println!("{:?}", i); + let mut child = cmd.spawn()?; + let out = child.stdout.take().unwrap(); + let mut out = std::io::BufReader::new(out); + let mut s = String::new(); + let mut n = 0; + while let Ok(_) = out.read_line(&mut s) { + // 进程退出后结束循环 + if let Ok(Some(_)) = child.try_wait() { + break; + } + println!("\n{}\n", s); + loading.text(format!("Building {}", n)); + n += 1; } loading.success("OK"); loading.end(); - if !output.status.success() { - log::error!("Build failed!"); - let reason = String::from_utf8_lossy(&output.stderr).to_string(); - return Err(Error::BuildFailed(reason)); - } + // 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 let bindgen_outdir = out_dir.join("assets").join("dioxus"); diff --git a/src/cargo.rs b/src/cargo.rs index 251e9d848..9994d5be5 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -1,4 +1,6 @@ //! Utilities for working with cargo and rust files +use serde::Serialize; + use crate::error::{Error, Result}; use std::{ env, fs, @@ -90,3 +92,26 @@ impl Metadata { Err(Error::CargoError("InvalidOutput".to_string())) } } + +#[derive(Serialize, Debug, Clone)] +pub struct CargoFormatInfo { + pub reason: CargoFormatReason, + #[serde(default)] + pub package_id: String, + #[serde(default)] + manifest_path: String, + #[serde(default)] + message: Option +} + +#[derive(Serialize, Debug, Clone)] +pub enum CargoFormatReason { + #[serde(rename = "compiler-message")] + CompilerMessage, + #[serde(rename = "compiler-artifact")] + CompilerArtifact, + #[serde(rename = "build-script-executed")] + BuildScriptExecuted, + #[serde(rename = "build-finished")] + BuildFinished, +} \ No newline at end of file From 0a2bac87154f8c867fc0427fabe21ce04fd3b026 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 00:28:00 +0800 Subject: [PATCH 168/516] feat: commit code --- src/builder.rs | 21 +++++++++++++-------- src/cargo.rs | 10 ++++++---- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index a662f8a20..bdb82fc45 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2,11 +2,11 @@ use crate::{ config::{CrateConfig, ExecutableType}, error::{Error, Result}, tools::Tool, - DioxusConfig, + CargoFormatInfo, DioxusConfig, }; use std::{ fs::{copy, create_dir_all, remove_dir_all, File}, - io::{Read, BufRead}, + io::{BufRead, Read}, panic, path::PathBuf, process::Command, @@ -80,16 +80,21 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { let mut child = cmd.spawn()?; let out = child.stdout.take().unwrap(); let mut out = std::io::BufReader::new(out); - let mut s = String::new(); - let mut n = 0; - while let Ok(_) = out.read_line(&mut s) { + let mut content = String::new(); + while let Ok(_) = out.read_line(&mut content) { // 进程退出后结束循环 if let Ok(Some(_)) = child.try_wait() { break; } - println!("\n{}\n", s); - loading.text(format!("Building {}", n)); - n += 1; + let content = content.split('\n').collect::>(); + for json in content { + let d = serde_json::from_str::(&json); + if let Ok(d) = d { + println!("\n{:#?}\n", d); + } else { + println!("\n\n{:?}\n\n", json); + } + } } loading.success("OK"); loading.end(); diff --git a/src/cargo.rs b/src/cargo.rs index 9994d5be5..6c5085066 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -1,5 +1,5 @@ //! Utilities for working with cargo and rust files -use serde::Serialize; +use serde::{Serialize, Deserialize}; use crate::error::{Error, Result}; use std::{ @@ -93,7 +93,7 @@ impl Metadata { } } -#[derive(Serialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct CargoFormatInfo { pub reason: CargoFormatReason, #[serde(default)] @@ -101,10 +101,12 @@ pub struct CargoFormatInfo { #[serde(default)] manifest_path: String, #[serde(default)] - message: Option + target: Option, + #[serde(default)] + message: Option, } -#[derive(Serialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum CargoFormatReason { #[serde(rename = "compiler-message")] CompilerMessage, From eb0145173d0f8d5db715875d3213499172e89ea8 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 00:32:23 +0800 Subject: [PATCH 169/516] feat: add `indicatif` crate --- Cargo.toml | 2 +- src/builder.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7e982bc78..a50a06fde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ regex = "1.5.4" chrono = "0.4.19" anyhow = "1.0.53" hyper = "0.14.17" -loading = "0.3.0" +indicatif = "0.16.2" axum = { version = "0.5.1", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/builder.rs b/src/builder.rs index bdb82fc45..ed0921cd1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -75,8 +75,6 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { ExecutableType::Example(name) => cmd.arg("--example").arg(name), }; - let loading = loading::Loading::default(); - loading.text("Building"); let mut child = cmd.spawn()?; let out = child.stdout.take().unwrap(); let mut out = std::io::BufReader::new(out); @@ -96,8 +94,6 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { } } } - loading.success("OK"); - loading.end(); // if !output.status.success() { // log::error!("Build failed!"); From dfe571df1a6260f31eff5f3e83113c448786e3b1 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 10:09:25 +0800 Subject: [PATCH 170/516] feat: commit prettier_build --- Cargo.toml | 2 +- src/builder.rs | 58 ++++++++++++++++++++++++++++++++------------------ src/cargo.rs | 27 ----------------------- 3 files changed, 38 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a50a06fde..7e982bc78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ regex = "1.5.4" chrono = "0.4.19" anyhow = "1.0.53" hyper = "0.14.17" -indicatif = "0.16.2" +loading = "0.3.0" axum = { version = "0.5.1", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/builder.rs b/src/builder.rs index ed0921cd1..5ef084326 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2,11 +2,14 @@ use crate::{ config::{CrateConfig, ExecutableType}, error::{Error, Result}, tools::Tool, - CargoFormatInfo, DioxusConfig, + DioxusConfig, }; +use cargo_metadata::Message; +use loading::Loading; use std::{ + collections::HashMap, fs::{copy, create_dir_all, remove_dir_all, File}, - io::{BufRead, Read}, + io::Read, panic, path::PathBuf, process::Command, @@ -75,25 +78,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { ExecutableType::Example(name) => cmd.arg("--example").arg(name), }; - let mut child = cmd.spawn()?; - let out = child.stdout.take().unwrap(); - let mut out = std::io::BufReader::new(out); - let mut content = String::new(); - while let Ok(_) = out.read_line(&mut content) { - // 进程退出后结束循环 - if let Ok(Some(_)) = child.try_wait() { - break; - } - let content = content.split('\n').collect::>(); - for json in content { - let d = serde_json::from_str::(&json); - if let Ok(d) = d { - println!("\n{:#?}\n", d); - } else { - println!("\n\n{:?}\n\n", json); - } - } - } + prettier_build(cmd).unwrap(); // if !output.status.success() { // log::error!("Build failed!"); @@ -342,6 +327,37 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { Ok(()) } +fn prettier_build(mut cmd: Command) -> anyhow::Result<()> { + let loading = Loading::default(); + let mut record: HashMap = HashMap::new(); + 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) => { + // println!("{:#?}", msg); + } + Message::CompilerArtifact(artifact) => { + record.insert(artifact.package_id.to_string(), artifact.target.name); + } + Message::BuildScriptExecuted(script) => { + let package_id = script.package_id.to_string(); + if let Some(name) = record.get(&package_id) { + loading.text(format!("[{}] build done", name)); + } + } + Message::BuildFinished(finished) => { + if finished.success { + } else { + std::process::exit(1); + } + } + _ => (), // Unknown message + } + } + Ok(()) +} + pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { let crate_root = crate::cargo::crate_root().unwrap(); let custom_html_file = crate_root.join("index.html"); diff --git a/src/cargo.rs b/src/cargo.rs index 6c5085066..251e9d848 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -1,6 +1,4 @@ //! Utilities for working with cargo and rust files -use serde::{Serialize, Deserialize}; - use crate::error::{Error, Result}; use std::{ env, fs, @@ -92,28 +90,3 @@ impl Metadata { Err(Error::CargoError("InvalidOutput".to_string())) } } - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct CargoFormatInfo { - pub reason: CargoFormatReason, - #[serde(default)] - pub package_id: String, - #[serde(default)] - manifest_path: String, - #[serde(default)] - target: Option, - #[serde(default)] - message: Option, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum CargoFormatReason { - #[serde(rename = "compiler-message")] - CompilerMessage, - #[serde(rename = "compiler-artifact")] - CompilerArtifact, - #[serde(rename = "build-script-executed")] - BuildScriptExecuted, - #[serde(rename = "build-finished")] - BuildFinished, -} \ No newline at end of file From 98134cfe1ce33d12ab1dcba2fa1ca71b05e8a682 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 10:09:44 +0800 Subject: [PATCH 171/516] feat: commit Cargo.lock --- .gitignore | 1 - Cargo.lock | 4684 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 4684 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 2e049011e..6b39d31a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target -Cargo.lock .idea/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..086673b7a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4684 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-task" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" + +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atk" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.2", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.13.0", + "bitflags", + "bytes", + "futures-util", + "headers", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sha-1 0.10.0", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-http 0.3.4", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", +] + +[[package]] +name = "backtrace" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +dependencies = [ + "byteorder", + "safemem", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64ct" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" + +[[package]] +name = "binary-install" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5bc5f8c50dd6a80d0b303ddab79f42ddcb52fd43d68107ecf622c551fd4cd4" +dependencies = [ + "curl", + "dirs 1.0.5", + "failure", + "flate2", + "hex", + "is_executable", + "siphasher", + "tar", + "zip 0.5.13", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array 0.14.5", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + +[[package]] +name = "cairo-rs" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +dependencies = [ + "glib-sys", + "libc", + "system-deps 6.0.2", +] + +[[package]] +name = "camino" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.12", + "serde", + "serde_json", +] + +[[package]] +name = "cargo_toml" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5809dd3e6444651fd1cdd3dbec71eca438c439a0fcc8081674a14da0afe50185" +dependencies = [ + "serde", + "serde_derive", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-expr" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.5", +] + +[[package]] +name = "clap" +version = "3.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "combine" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "crypto-common" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0" +dependencies = [ + "generic-array 0.14.5", + "typenum", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "curl" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.55+curl-7.83.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core 0.10.2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.2.4" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-desktop", + "dioxus-hooks", + "dioxus-html", + "dioxus-router", + "dioxus-rsx", + "dioxus-rsx-interpreter", + "dioxus-web", +] + +[[package]] +name = "dioxus-cli" +version = "0.1.5" +dependencies = [ + "anyhow", + "atty", + "axum", + "binary-install", + "cargo_metadata", + "cargo_toml", + "chrono", + "clap", + "colored 2.0.0", + "convert_case", + "dioxus", + "dirs 4.0.0", + "fern", + "flate2", + "fs_extra", + "futures", + "headers", + "html_parser", + "hyper", + "loading", + "log", + "notify", + "proc-macro2", + "regex", + "reqwest", + "serde", + "serde_json", + "syn", + "tar", + "thiserror", + "tokio", + "toml", + "tower", + "tower-http 0.2.5", + "walkdir", + "wasm-bindgen-cli-support", + "zip 0.6.2", +] + +[[package]] +name = "dioxus-core" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "backtrace", + "bumpalo", + "futures-channel", + "futures-util", + "fxhash", + "indexmap", + "log", + "longest-increasing-subsequence", + "once_cell", + "serde", + "slab", + "smallvec", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-rsx", + "dioxus-rsx-interpreter", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dioxus-desktop" +version = "0.2.3" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "core-foundation", + "dioxus-core", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-rsx-interpreter", + "dunce", + "futures-channel", + "interprocess", + "log", + "mime_guess", + "serde", + "serde_json", + "thiserror", + "tokio", + "webbrowser", + "wry", +] + +[[package]] +name = "dioxus-hooks" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-core", + "futures-channel", + "log", +] + +[[package]] +name = "dioxus-html" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-core", + "enumset", + "euclid", + "keyboard-types", + "serde", + "serde_repr", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-interpreter-js" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "dioxus-router" +version = "0.2.3" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "futures-channel", + "futures-util", + "log", + "rsx-prelude", + "serde", + "serde_urlencoded", + "thiserror", + "url", +] + +[[package]] +name = "dioxus-rsx" +version = "0.0.0" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dioxus-rsx-interpreter" +version = "0.1.0" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-core", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-ssr", + "lazy_static", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "dioxus-ssr" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-core", +] + +[[package]] +name = "dioxus-web" +version = "0.2.1" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "anyhow", + "console_error_panic_hook", + "dioxus-core", + "dioxus-html", + "dioxus-interpreter-js", + "dioxus-rsx-interpreter", + "futures-channel", + "futures-util", + "fxhash", + "gloo-timers", + "js-sys", + "log", + "once_cell", + "serde-wasm-bindgen", + "serde_json", + "smallstr", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users 0.3.5", + "winapi", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users 0.4.3", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dunce" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-as-inner" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumset" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4799cdb24d48f1f8a7a98d06b7fde65a85a2d1e42b25a889f5406aa1fbefe074" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea83a3fbdc1d999ccfbcbee717eab36f8edf2d71693a23ce0d7cca19e085304c" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "euclid" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "event-listener" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fern" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" +dependencies = [ + "colored 1.9.3", + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.13", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +dependencies = [ + "bitflags", + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.2", +] + +[[package]] +name = "gdk-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps 6.0.2", +] + +[[package]] +name = "gdkx11-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps 6.0.2", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "gio" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "gio-sys", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.2", + "winapi", +] + +[[package]] +name = "glib" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +dependencies = [ + "anyhow", + "heck 0.4.0", + "proc-macro-crate 1.1.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +dependencies = [ + "libc", + "system-deps 6.0.2", +] + +[[package]] +name = "gloo-timers" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gobject-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +dependencies = [ + "glib-sys", + "libc", + "system-deps 6.0.2", +] + +[[package]] +name = "gtk" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps 6.0.2", +] + +[[package]] +name = "gtk3-macros" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" +dependencies = [ + "anyhow", + "proc-macro-crate 1.1.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64 0.13.0", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1 0.10.0", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "html_parser" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec016cabcf7c9c48f9d5fdc6b03f273585bfce640a0f47a69552039e92b1959a" +dependencies = [ + "pest", + "pest_derive", + "serde", + "serde_derive", + "serde_json", + "thiserror", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interprocess" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c58ec7fbda1df9a93f587b780659db3c99f61f4be27f9c82c9b37684ffd0366" +dependencies = [ + "blocking", + "cfg-if", + "futures", + "intmap", + "libc", + "once_cell", + "spinning", + "thiserror", + "winapi", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + +[[package]] +name = "ipconfig" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +dependencies = [ + "socket2", + "widestring", + "winapi", + "winreg 0.7.0", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "is_executable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d553b8abc8187beb7d663e34c065ac4570b273bc9511a50e940e99409c577" +dependencies = [ + "winapi", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "javascriptcore-rs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +dependencies = [ + "bitflags", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "jni" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24967112a1e4301ca5342ea339763613a37592b8a6ce6cf2e4494537c7a42faf" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" +dependencies = [ + "bitflags", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kqueue" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6112e8f37b59803ac47a42d14f1f3a59bbf72fc6857ffc5be455e28a691f8e" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libayatana-appindicator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0379d22a323947cb27b4dbe3b14fc1dcde07c9e9c3eca52ddf97ca0819c28cc4" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "log", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "loading" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "676420b03272176423578148eee61319e893422694f6d96a394841cb0901357f" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64d6af06fde0e527b1ba5c7b79a6cc89cfc46325b0b2887dffe8f70197e0c3c" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.2.2", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.3.0", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-glue" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3648f3609716eb7dbf5f5b5d4b84fcd67dd4c34efcdb12e4a6c0929c2ac48349" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.4.0", + "ndk-context", + "ndk-macro 0.2.0", + "ndk-sys 0.2.2", +] + +[[package]] +name = "ndk-glue" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.6.0", + "ndk-context", + "ndk-macro 0.3.0", + "ndk-sys 0.3.0", +] + +[[package]] +name = "ndk-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" +dependencies = [ + "darling 0.10.2", + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" +dependencies = [ + "darling 0.13.4", + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "notify" +version = "5.0.0-pre.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "553f9844ad0b0824605c20fb55a661679782680410abfb1a8144c2e7e437e7a7" +dependencies = [ + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "serde", + "walkdir", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" + +[[package]] +name = "pango" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +dependencies = [ + "bitflags", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.2", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.13", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.13", + "smallvec", + "windows-sys", +] + +[[package]] +name = "password-hash" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest 0.10.3", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + +[[package]] +name = "pin-project" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +dependencies = [ + "cty", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom 0.1.16", + "redox_syscall 0.1.57", + "rust-argon2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.7", + "redox_syscall 0.2.13", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64 0.13.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower-service", + "trust-dns-resolver", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg 0.10.1", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rsx-prelude" +version = "0.1.0" +source = "git+https://github.com/dioxuslabs/dioxus/#baaa1752d3ccd5918fc472b1071b95a12b019687" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx-interpreter", +] + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64 0.13.0", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +dependencies = [ + "base64 0.13.0", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfc62771e7b829b517cb213419236475f434fb480eddd76112ae182d274434a" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "smallstr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e922794d168678729ffc7e07182721a14219c65814e66e91b839a272fe5ae4f" +dependencies = [ + "smallvec", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soup2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +dependencies = [ + "bitflags", + "gio", + "glib", + "libc", + "once_cell", + "soup2-sys", +] + +[[package]] +name = "soup2-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +dependencies = [ + "bitflags", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "system-deps" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" +dependencies = [ + "cfg-expr 0.9.1", + "heck 0.3.3", + "pkg-config", + "toml", + "version-compare 0.0.11", +] + +[[package]] +name = "system-deps" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709" +dependencies = [ + "cfg-expr 0.10.3", + "heck 0.4.0", + "pkg-config", + "toml", + "version-compare 0.1.0", +] + +[[package]] +name = "tao" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895eb66ff23d1824055114d6fbdd2c297bf70ad85349a4adaee0f8b2a860d1" +dependencies = [ + "bitflags", + "cairo-rs", + "cc", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "gdk", + "gdk-pixbuf", + "gdk-sys", + "gdkx11-sys", + "gio", + "glib", + "glib-sys", + "gtk", + "instant", + "lazy_static", + "libayatana-appindicator", + "libc", + "log", + "ndk 0.4.0", + "ndk-glue 0.4.2", + "ndk-sys 0.2.2", + "objc", + "parking_lot 0.11.2", + "raw-window-handle", + "scopeguard", + "serde", + "tao-core-video-sys", + "unicode-segmentation", + "windows", + "windows_macros", + "x11-dl", +] + +[[package]] +name = "tao-core-video-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "objc", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall 0.2.13", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "trust-dns-proto" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "lazy_static", + "log", + "rand", + "smallvec", + "thiserror", + "tinyvec", + "tokio", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "parking_lot 0.12.1", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "trust-dns-proto", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" +dependencies = [ + "base64 0.13.0", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand", + "sha-1 0.10.0", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + +[[package]] +name = "version-compare" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "walrus" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb08e48cde54c05f363d984bb54ce374f49e242def9468d2e1b6c2372d291f8" +dependencies = [ + "anyhow", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasmparser 0.77.0", +] + +[[package]] +name = "walrus-macro" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-cli-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4016fbd42224de21aab2f009aeaec61067d278a298ba7f8f7f8d40fbffea0822" +dependencies = [ + "anyhow", + "base64 0.9.3", + "log", + "rustc-demangle", + "serde_json", + "tempfile", + "walrus", + "wasm-bindgen-externref-xform", + "wasm-bindgen-multi-value-xform", + "wasm-bindgen-shared", + "wasm-bindgen-threads-xform", + "wasm-bindgen-wasm-conventions", + "wasm-bindgen-wasm-interpreter", + "wit-text", + "wit-validator", + "wit-walrus", +] + +[[package]] +name = "wasm-bindgen-externref-xform" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33c8e2d3f3b6f6647f982911eb4cb44998c8cca97a4fe7afc99f616ebb33a73" +dependencies = [ + "anyhow", + "walrus", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-multi-value-xform" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7015b54357604811162710d5cf274ab85d974fe1e324222dd5b2133afdefe9b9" +dependencies = [ + "anyhow", + "walrus", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "wasm-bindgen-threads-xform" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6961b838d9a9c121ba4a1eea1628014cc759469e3defb42bbac9c5ed0f65be14" +dependencies = [ + "anyhow", + "walrus", + "wasm-bindgen-wasm-conventions", +] + +[[package]] +name = "wasm-bindgen-wasm-conventions" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a0eca38fe89471f57d6903f3e17e732d2d6f995a7af5b23f27df7fee0f0d18" +dependencies = [ + "anyhow", + "walrus", +] + +[[package]] +name = "wasm-bindgen-wasm-interpreter" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1c9fb7f71137840932bbb853ef1f83d68c88584b716c9bbae38675c9fb8b86" +dependencies = [ + "anyhow", + "log", + "walrus", + "wasm-bindgen-wasm-conventions", +] + +[[package]] +name = "wasmparser" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a950e6a618f62147fd514ff445b2a0b53120d382751960797f85f058c7eda9b9" + +[[package]] +name = "wasmparser" +version = "0.77.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6" + +[[package]] +name = "wast" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1844f66a2bc8526d71690104c0e78a8e59ffa1597b7245769d174ebb91deb5" +dependencies = [ + "leb128", +] + +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a3cffdb686fbb24d9fb8f03a213803277ed2300f11026a3afe1f108dc021b" +dependencies = [ + "jni 0.19.0", + "ndk-glue 0.6.2", + "url", + "web-sys", + "widestring", + "winapi", +] + +[[package]] +name = "webkit2gtk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29952969fb5e10fe834a52eb29ad0814ccdfd8387159b0933edf1344a1c9cdcc" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup2", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +dependencies = [ + "atk-sys", + "bitflags", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pango-sys", + "pkg-config", + "soup2-sys", + "system-deps 6.0.2", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +dependencies = [ + "webpki", +] + +[[package]] +name = "webview2-com" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8e90ac2d9ce39cdb70017aaec641be09fbdd702b7b332b9896d053eb469524" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows_macros", +] + +[[package]] +name = "webview2-com-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515c6c82fcee93f6edaacc72c8e233dbe4ff3ca569dce1901dfc36c404a3e99" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "webview2-com-sys" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92160310b3322397e4ff8a8285a7429d73a07a68fda44ee80879605b93e53f76" +dependencies = [ + "regex", + "serde", + "serde_json", + "thiserror", + "windows", + "windows-bindgen", +] + +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" +dependencies = [ + "windows_aarch64_msvc 0.30.0", + "windows_i686_gnu 0.30.0", + "windows_i686_msvc 0.30.0", + "windows_x86_64_gnu 0.30.0", + "windows_x86_64_msvc 0.30.0", +] + +[[package]] +name = "windows-bindgen" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944c545fcae9dd66488308f8b69aa3ba34f53714416ecfcdcbbfa4b6821e27c6" +dependencies = [ + "windows_quote", + "windows_reader", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_gen" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30dff4d91d22520628bb94b66f2bb313cb16a09a515a32320a84a1b449bc94c0" +dependencies = [ + "windows_quote", + "windows_reader", +] + +[[package]] +name = "windows_i686_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_macros" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ae44ab917e9005fe710d99d52d227ca0164b10a09be90649142cc3fab825d3" +dependencies = [ + "syn", + "windows_gen", + "windows_quote", + "windows_reader", +] + +[[package]] +name = "windows_quote" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71f02c51a77e6248c1206aaa920802c32d50a05205e229b118d7f3afd3036667" + +[[package]] +name = "windows_reader" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44e6df0da993cda589c5ac852272fbb2a0ead67a031a017dd3eac11528a2d72" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wit-parser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5fd97866f4b9c8e1ed57bcf9446f3d0d8ba37e2dd01c3c612c046c053b06f7" +dependencies = [ + "anyhow", + "leb128", + "wit-schema-version", +] + +[[package]] +name = "wit-schema-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfee4a6a4716eefa0682e7a3b836152e894a3e4f34a9d6c2c3e1c94429bfe36a" + +[[package]] +name = "wit-text" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33358e95c77d660f1c7c07f4a93c2bd89768965e844e3c50730bb4b42658df5f" +dependencies = [ + "anyhow", + "wast", + "wit-writer", +] + +[[package]] +name = "wit-validator" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c11d93d925420e7872b226c4161849c32be38385ccab026b88df99d8ddc6ba6" +dependencies = [ + "anyhow", + "wasmparser 0.59.0", + "wit-parser", + "wit-schema-version", +] + +[[package]] +name = "wit-walrus" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad559e3e4c6404b2a6a675d44129d62a3836e3b951b90112fa1c5feb852757cd" +dependencies = [ + "anyhow", + "id-arena", + "walrus", + "wit-parser", + "wit-schema-version", + "wit-writer", +] + +[[package]] +name = "wit-writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ad01ba5e9cbcff799a0689e56a153776ea694cec777f605938cb9880d41a09" +dependencies = [ + "leb128", + "wit-schema-version", +] + +[[package]] +name = "wry" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5676092e1a33448ed0f268717bcbb2e928354b78f4c1f60f3d43641eedea0d9" +dependencies = [ + "block", + "cocoa", + "core-graphics", + "gdk", + "gio", + "glib", + "gtk", + "http", + "jni 0.18.0", + "libc", + "log", + "objc", + "objc_id", + "once_cell", + "serde", + "serde_json", + "tao", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows_macros", +] + +[[package]] +name = "x11" +version = "2.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd0565fa8bfba8c5efe02725b14dff114c866724eff2cfd44d76cea74bcd87a" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +dependencies = [ + "lazy_static", + "libc", + "pkg-config", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time 0.1.44", +] + +[[package]] +name = "zip" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time 0.3.11", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.10.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4a6bd64f22b5e3e94b4e238669ff9f10815c27a5180108b849d24174a83847" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.6+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b61c51bb270702d6167b8ce67340d2754b088d0c091b06e593aa772c3ee9bb" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8" +dependencies = [ + "cc", + "libc", +] From 526a88cab16045cf6b8acba75471cc62f17dca10 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 14:16:19 +0800 Subject: [PATCH 172/516] feat: commit code --- Cargo.lock | 66 +++++++++++++++++++++++++++++----- Cargo.toml | 4 +-- src/builder.rs | 73 +++++++++++++++++++++++-------------- src/cli/serve/mod.rs | 36 ++++++++----------- src/server/mod.rs | 86 +++++++++++++++++++++++++++++++++++--------- 5 files changed, 190 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 086673b7a..4c4720564 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" dependencies = [ "camino", "cargo-platform", @@ -590,6 +590,21 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "console" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -872,7 +887,7 @@ dependencies = [ "headers", "html_parser", "hyper", - "loading", + "indicatif", "log", "notify", "proc-macro2", @@ -1101,6 +1116,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -1906,6 +1927,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.17.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017d0ce94b8e91e29d2c78ed891e57e5ec3dc4371820a9d96abab4af09eb8ad" +dependencies = [ + "console", + "number_prefix", + "unicode-width", +] + [[package]] name = "inotify" version = "0.9.6" @@ -2145,12 +2177,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "loading" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "676420b03272176423578148eee61319e893422694f6d96a394841cb0901357f" - [[package]] name = "lock_api" version = "0.4.7" @@ -2469,6 +2495,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "objc" version = "0.2.7" @@ -3525,6 +3557,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.15.0" @@ -3893,6 +3935,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 7e982bc78..da2467fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,14 +29,14 @@ notify = { version = "5.0.0-pre.13", features = ["serde"] } html_parser = "0.6.2" binary-install = "0.0.2" convert_case = "0.5.0" -cargo_metadata = "0.14.1" +cargo_metadata = "0.15.0" tokio = { version = "1.16.1", features = ["full"] } atty = "0.2.14" regex = "1.5.4" chrono = "0.4.19" anyhow = "1.0.53" hyper = "0.14.17" -loading = "0.3.0" +indicatif = "0.17.0-rc.11" axum = { version = "0.5.1", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/builder.rs b/src/builder.rs index 5ef084326..d164e5418 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -4,19 +4,26 @@ use crate::{ tools::Tool, DioxusConfig, }; -use cargo_metadata::Message; -use loading::Loading; +use cargo_metadata::{diagnostic::Diagnostic, Message}; +use indicatif::{ProgressBar, ProgressStyle}; +use serde::Serialize; use std::{ - collections::HashMap, fs::{copy, create_dir_all, remove_dir_all, File}, io::Read, panic, path::PathBuf, process::Command, + time::Duration, }; use wasm_bindgen_cli_support::Bindgen; -pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { +#[derive(Serialize, Debug, Clone)] +pub struct BuildResult { + pub warnings: Vec, + pub elapsed_time: u128, +} + +pub fn build(config: &CrateConfig, quiet: bool) -> Result { // [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 // [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm @@ -78,13 +85,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { ExecutableType::Example(name) => cmd.arg("--example").arg(name), }; - prettier_build(cmd).unwrap(); - - // if !output.status.success() { - // log::error!("Build failed!"); - // let reason = String::from_utf8_lossy(&output.stderr).to_string(); - // return Err(Error::BuildFailed(reason)); - // } + let warning_messages = prettier_build(cmd)?; // [2] Establish the output directory structure let bindgen_outdir = out_dir.join("assets").join("dioxus"); @@ -122,8 +123,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { .unwrap(); }); 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 Ok(()); + 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())); } // check binaryen:wasm-opt tool @@ -193,8 +193,10 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { } let t_end = std::time::Instant::now(); - log::info!("🏁 Done in {}ms!", (t_end - t_start).as_millis()); - Ok(()) + Ok(BuildResult { + warnings: warning_messages, + elapsed_time: (t_end - t_start).as_millis() + }) } pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { @@ -327,27 +329,46 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { Ok(()) } -fn prettier_build(mut cmd: Command) -> anyhow::Result<()> { - let loading = Loading::default(); - let mut record: HashMap = HashMap::new(); +fn prettier_build(mut cmd: Command) -> anyhow::Result> { + let mut warning_messages: Vec = 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("Start to build 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) => { - // println!("{:#?}", msg); + Message::CompilerMessage(msg) => { + let message = msg.message; + match message.level { + cargo_metadata::diagnostic::DiagnosticLevel::Error => { + log::error!("Build Failed: {}", message.message); + std::process::exit(1); + } + cargo_metadata::diagnostic::DiagnosticLevel::Warning => { + warning_messages.push(message.clone()); + } + _ => {} + } } Message::CompilerArtifact(artifact) => { - record.insert(artifact.package_id.to_string(), artifact.target.name); + pb.set_message(format!("Compiling {} ", artifact.package_id)); + pb.tick(); } Message::BuildScriptExecuted(script) => { - let package_id = script.package_id.to_string(); - if let Some(name) = record.get(&package_id) { - loading.text(format!("[{}] build done", name)); - } + let _package_id = script.package_id.to_string(); } Message::BuildFinished(finished) => { if finished.success { + pb.finish_and_clear(); + println!("Done in"); } else { std::process::exit(1); } @@ -355,7 +376,7 @@ fn prettier_build(mut cmd: Command) -> anyhow::Result<()> { _ => (), // Unknown message } } - Ok(()) + Ok(warning_messages) } pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 62843d653..93770b6ed 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -38,31 +38,25 @@ impl Serve { .clone() }); - match platform.as_str() { - "web" => { - crate::builder::build(&crate_config, false)?; - } - "desktop" => { - crate::builder::build_desktop(&crate_config, true)?; + if platform.as_str() == "desktop" { + crate::builder::build_desktop(&crate_config, true)?; - match &crate_config.executable { - crate::ExecutableType::Binary(name) - | crate::ExecutableType::Lib(name) - | crate::ExecutableType::Example(name) => { - let mut file = crate_config.out_dir.join(name); - if cfg!(windows) { - file.set_extension("exe"); - } - Command::new(crate_config.out_dir.join(file).to_str().unwrap()) - .stdout(Stdio::inherit()) - .output()?; + match &crate_config.executable { + crate::ExecutableType::Binary(name) + | crate::ExecutableType::Lib(name) + | crate::ExecutableType::Example(name) => { + let mut file = crate_config.out_dir.join(name); + if cfg!(windows) { + file.set_extension("exe"); } + 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 diff --git a/src/server/mod.rs b/src/server/mod.rs index 3ecce6c26..b20d84db3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,6 +6,7 @@ use axum::{ routing::{get, get_service}, Router, }; +use cargo_metadata::diagnostic::Diagnostic; use colored::Colorize; use dioxus::rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; @@ -15,7 +16,7 @@ use std::{net::UdpSocket, path::PathBuf, process::Command, sync::Arc}; use tower::ServiceBuilder; 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; mod hot_reload; @@ -27,9 +28,9 @@ pub struct BuildManager { } impl BuildManager { - fn rebuild(&self) -> Result<()> { + fn rebuild(&self) -> Result { log::info!("🪁 Rebuild project"); - builder::build(&self.config, true)?; + let result = builder::build(&self.config, true)?; // change the websocket reload state to true; // the page will auto-reload. if self @@ -43,7 +44,7 @@ impl BuildManager { let _ = Serve::regen_dev_page(&self.config); } let _ = self.reload_tx.send(()); - Ok(()) + Ok(result) } } @@ -61,6 +62,8 @@ pub async fn startup(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..."); let dist_path = config.out_dir.clone(); @@ -159,9 +162,22 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } } if needs_rebuild { - print_console_info(port, &config, evt.paths); - if let Err(err) = build_manager.rebuild() { - log::error!("{}", err); + match build_manager.rebuild() { + Ok(res) => { + print_console_info( + port, + &config, + PrettierOptions { + changed: evt.paths, + warnings: res.warnings, + elapsed_time: res.elapsed_time, + }, + ); + } + Err(err) => { + log::error!("{}", err); + std::process::exit(1); + } } } if !messages.is_empty() { @@ -183,7 +199,15 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0:8080 - print_console_info(port, &config, vec![]); + 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 = ServiceBuilder::new() @@ -246,6 +270,8 @@ pub async fn startup_hot_reload(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..."); let dist_path = config.out_dir.clone(); @@ -276,10 +302,20 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { let mut watcher = RecommendedWatcher::new(move |info: notify::Result| { let config = watcher_config.clone(); if info.is_ok() { - print_console_info(port, &config, info.unwrap().paths); if chrono::Local::now().timestamp() > last_update_time { match build_manager.rebuild() { - Ok(_) => last_update_time = chrono::Local::now().timestamp(), + 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), } } @@ -297,8 +333,16 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0 - print_console_info(port, &config, vec![]); - + 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 = ServiceBuilder::new() .and_then( @@ -355,7 +399,14 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { Ok(()) } -fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { +#[derive(Debug, Default)] +pub struct PrettierOptions { + changed: Vec, + warnings: Vec, + elapsed_time: u128, +} + +fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) { print!( "{}", String::from_utf8_lossy( @@ -413,7 +464,7 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { "False" }; - if changed.len() <= 0 { + if options.changed.len() <= 0 { println!( "{} @ v{}\n", "Dioxus".bold().green(), @@ -422,7 +473,9 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { } else { println!( "Project Reloaded: {}\n", - format!("Changed {} files.", changed.len()).purple().bold() + format!("Changed {} files.", options.changed.len()) + .purple() + .bold() ); } println!( @@ -446,12 +499,11 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { println!("\t> URL Rewrite [index_on_404] : {}", url_rewrite.purple()); println!(""); - if changed.len() <= 0 { + if options.changed.len() <= 0 { log::info!("{}", "Server startup completed.".green().bold()); } else { log::info!("{}", "Project rebuild completed.".green().bold()); } - } fn get_ip() -> Option { From 78c78548e80c1be44589409f3b99f065aa77c781 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 16:41:53 +0800 Subject: [PATCH 173/516] feat: commit prettier_build --- src/builder.rs | 2 +- src/server/mod.rs | 42 +++++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index d164e5418..33ce411f6 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -368,7 +368,7 @@ fn prettier_build(mut cmd: Command) -> anyhow::Result> { Message::BuildFinished(finished) => { if finished.success { pb.finish_and_clear(); - println!("Done in"); + log::info!("Build done."); } else { std::process::exit(1); } diff --git a/src/server/mod.rs b/src/server/mod.rs index b20d84db3..5cd5cdcb7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -342,7 +342,7 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { elapsed_time: first_build_result.elapsed_time, }, ); - + let file_service_config = config.clone(); let file_service = ServiceBuilder::new() .and_then( @@ -434,18 +434,6 @@ fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) profile = config.custom_profile.as_ref().unwrap().to_string(); } let hot_reload = if config.hot_reload { "RSX" } else { "Normal" }; - let tools_str = format!( - "{:?}", - config - .dioxus_config - .application - .tools - .clone() - .unwrap_or_default() - .iter() - .map(|d| d.0.clone()) - .collect::>() - ); let crate_root = crate::cargo::crate_root().unwrap(); let custom_html_file = if crate_root.join("index.html").is_file() { "Custom [index.html]" @@ -494,15 +482,35 @@ fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) println!(""); println!("\t> Profile : {}", profile.green()); println!("\t> Hot Reload : {}", hot_reload.cyan()); - println!("\t> Enabled Tools : {}", tools_str.yellow()); 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.changed.len() <= 0 { - log::info!("{}", "Server startup completed.".green().bold()); + if options.warnings.len() == 0 { + log::info!("{}\n", "A perfect compilation!".green().bold()); } else { - log::info!("{}", "Project rebuild completed.".green().bold()); + log::warn!( + "{}\n", + format!( + "There were {} warning messages during the build: ", + options.warnings.len() - 1 + ) + .yellow() + .bold() + ); + for info in &options.warnings { + if info.message == format!("{} warnings emitted", options.warnings.len() - 1) { + continue; + } + if let Some(val) = info.rendered.clone() { + log::warn!("{}", val); + } + } } } From 8613518c40e722f5e9abfbb53ed7df034ab2b7a3 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:00:17 +0800 Subject: [PATCH 174/516] feat: commit code --- src/server/mod.rs | 58 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 5cd5cdcb7..614e8fa7a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -495,7 +495,7 @@ fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) log::info!("{}\n", "A perfect compilation!".green().bold()); } else { log::warn!( - "{}\n", + "{}", format!( "There were {} warning messages during the build: ", options.warnings.len() - 1 @@ -504,13 +504,63 @@ fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) .bold() ); for info in &options.warnings { - if info.message == format!("{} warnings emitted", options.warnings.len() - 1) { + let message = info.message.clone(); + if message == format!("{} warnings emitted", options.warnings.len() - 1) { continue; } - if let Some(val) = info.rendered.clone() { - log::warn!("{}", val); + 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 = format!( + // "[{}: {}]: {} --> {}", + // file, + // if line.0 == line.1 { + // line.0.to_string() + // } else { + // format!("{}~{}", line.0, line.1) + // }, + // if code.len() == 1 { + // code.get(0).unwrap().text.trim().blue().bold().to_string() + // } else { + // format!("\n{}",code.iter() + // .enumerate() + // .map(|(_i, s)| format!("\t{}\n", s.text).blue().bold().to_string()) + // .collect::()) + // }, + // message.yellow().bold() + // ); + 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::(); + format!("[{}: {}]:\n{}", file, line_str, code) + }; + console = format!("{console}\n\t{span_info}"); } + println!("{console}"); } + println!( + "\n{}\n", + "Resolving all warnings will help your code run better!".yellow() + ); } } From bfaa8e3c74aa82d2ad6a5b917af98f9e6cc729f8 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:54:16 +0800 Subject: [PATCH 175/516] feat: commit code --- src/server/mod.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 614e8fa7a..e5bffc17e 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -518,24 +518,6 @@ fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) format!("{}~{}", line.0, line.1) }; let code = span.text.clone(); - // let span_info = format!( - // "[{}: {}]: {} --> {}", - // file, - // if line.0 == line.1 { - // line.0.to_string() - // } else { - // format!("{}~{}", line.0, line.1) - // }, - // if code.len() == 1 { - // code.get(0).unwrap().text.trim().blue().bold().to_string() - // } else { - // format!("\n{}",code.iter() - // .enumerate() - // .map(|(_i, s)| format!("\t{}\n", s.text).blue().bold().to_string()) - // .collect::()) - // }, - // message.yellow().bold() - // ); let span_info = if code.len() == 1 { let code = code.get(0).unwrap().text.trim().blue().bold().to_string(); format!( @@ -551,7 +533,7 @@ fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) .enumerate() .map(|(_i, s)| format!("\t{}\n", s.text).blue().bold().to_string()) .collect::(); - format!("[{}: {}]:\n{}", file, line_str, code) + format!("[{}: {}]:\n{}\n#:{}", file, line_str, code, message) }; console = format!("{console}\n\t{span_info}"); } From d543ea19d0f2647a795b1cd32c243f0591e5f8f1 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:02:33 +0800 Subject: [PATCH 176/516] feat: commit some thing --- src/builder.rs | 9 ++++----- src/main.rs | 10 +++++----- src/server/mod.rs | 1 - 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 33ce411f6..31b99b5f4 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -195,7 +195,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { let t_end = std::time::Instant::now(); Ok(BuildResult { warnings: warning_messages, - elapsed_time: (t_end - t_start).as_millis() + elapsed_time: (t_end - t_start).as_millis(), }) } @@ -339,7 +339,7 @@ fn prettier_build(mut cmd: Command) -> anyhow::Result> { .unwrap() .tick_chars("/|\\- "), ); - pb.set_message("Start to build project"); + pb.set_message("💼 Waiting to strart build the project..."); let mut command = cmd.spawn()?; let reader = std::io::BufReader::new(command.stdout.take().unwrap()); @@ -349,8 +349,7 @@ fn prettier_build(mut cmd: Command) -> anyhow::Result> { let message = msg.message; match message.level { cargo_metadata::diagnostic::DiagnosticLevel::Error => { - log::error!("Build Failed: {}", message.message); - std::process::exit(1); + return Err(anyhow::anyhow!(message.rendered.unwrap_or("Unknown".into()))); } cargo_metadata::diagnostic::DiagnosticLevel::Warning => { warning_messages.push(message.clone()); @@ -368,7 +367,7 @@ fn prettier_build(mut cmd: Command) -> anyhow::Result> { Message::BuildFinished(finished) => { if finished.success { pb.finish_and_clear(); - log::info!("Build done."); + log::info!("👑 Build done."); } else { std::process::exit(1); } diff --git a/src/main.rs b/src/main.rs index 099ff74c0..39b360f7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,31 +9,31 @@ async fn main() -> Result<()> { match args.action { Commands::Translate(opts) => { if let Err(e) = opts.translate() { - log::error!("translate error: {}", e); + log::error!("🚫 Translate failed: {}", e); } } Commands::Build(opts) => { if let Err(e) = opts.build() { - log::error!("build error: {}", e); + log::error!("🚫 Build project failed: {}", e); } } Commands::Clean(opts) => { if let Err(e) = opts.clean() { - log::error!("clean error: {}", e); + log::error!("🚫 Clean project failed: {}", e); } } Commands::Serve(opts) => { if let Err(e) = opts.serve().await { - log::error!("serve error: {}", e); + log::error!("🚫 Serve startup failed: {}", e); } } Commands::Create(opts) => { if let Err(e) = opts.create() { - log::error!("create error: {}", e); + log::error!("🚫 Create project failed: {}", e); } } diff --git a/src/server/mod.rs b/src/server/mod.rs index e5bffc17e..8f50a6cc3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -176,7 +176,6 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } Err(err) => { log::error!("{}", err); - std::process::exit(1); } } } From dfcf4e1f88e6f8296a5691e13bd6a5122c64996d Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:17:32 +0800 Subject: [PATCH 177/516] feat: change default config --- README.md | 2 +- src/assets/dioxus.toml | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a939dfcaf..f8515f390 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-

📦✨ dioxus-cli

+

📦✨ Dioxus CLI

Tooling to supercharge dioxus projects

diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index a5e849bfb..5c4f49f98 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -19,6 +19,8 @@ asset_dir = "public" # HTML title tag content title = "dioxus | ⛺" +index_on_404 = true + [web.watcher] watch_path = ["src"] @@ -45,10 +47,12 @@ script = [] binaryen = { wasm_opt = true } # 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. -input = "*" +# input = "*" # or you can specify some scss file -> css file # input = [ @@ -57,4 +61,4 @@ input = "*" # "/css/test.scss" # ] -source_map = true \ No newline at end of file +# source_map = true \ No newline at end of file From c97d7f263a4e785db533f726c80aee548d6f1cc0 Mon Sep 17 00:00:00 2001 From: YuKun Liu <41265098+mrxiaozhuox@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:21:21 +0800 Subject: [PATCH 178/516] feat: commit code --- src/assets/dioxus.toml | 2 +- src/assets/index.html | 5 ++--- src/cli/create/mod.rs | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/assets/dioxus.toml b/src/assets/dioxus.toml index 5c4f49f98..649aa2c3c 100644 --- a/src/assets/dioxus.toml +++ b/src/assets/dioxus.toml @@ -17,7 +17,7 @@ asset_dir = "public" [web.app] # HTML title tag content -title = "dioxus | ⛺" +title = "Dioxus | An elegant GUI library for Rust" index_on_404 = true diff --git a/src/assets/index.html b/src/assets/index.html index 87d447816..ed3d48135 100644 --- a/src/assets/index.html +++ b/src/assets/index.html @@ -9,10 +9,9 @@
- ".to_string()), + dioxus_desktop::Config::new() + .with_custom_head(r#""#.to_string()), ); + #[cfg(target_arch = "wasm32")] + dioxus_web::launch(app); } pub fn app(cx: Scope) -> Element { diff --git a/examples/tailwind/tailwind.config.js b/examples/tailwind/tailwind.config.js new file mode 100644 index 000000000..2a69d5803 --- /dev/null +++ b/examples/tailwind/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + mode: "all", + content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], + theme: { + extend: {}, + }, + plugins: [], +}; From 7f6f6fb8c87d050d30e39d0bc2cf248d9996226e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 28 Mar 2023 13:35:17 -0500 Subject: [PATCH 312/516] create server package --- Cargo.toml | 1 + packages/server/Cargo.toml | 21 +++++ packages/server/server_macro/Cargo.toml | 8 ++ packages/server/server_macro/src/lib.rs | 67 +++++++++++++++ packages/server/src/adapters/axum_adapter.rs | 23 +++++ packages/server/src/adapters/salvo_adapter.rs | 25 ++++++ packages/server/src/adapters/warp_adapter.rs | 28 ++++++ packages/server/src/lib.rs | 85 +++++++++++++++++++ 8 files changed, 258 insertions(+) create mode 100644 packages/server/Cargo.toml create mode 100644 packages/server/server_macro/Cargo.toml create mode 100644 packages/server/server_macro/src/lib.rs create mode 100644 packages/server/src/adapters/axum_adapter.rs create mode 100644 packages/server/src/adapters/salvo_adapter.rs create mode 100644 packages/server/src/adapters/warp_adapter.rs create mode 100644 packages/server/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index e32940e18..3b36bb5e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "packages/rsx-rosetta", "packages/signals", "packages/hot-reload", + "packages/server", "docs/guide", ] diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml new file mode 100644 index 000000000..13704574b --- /dev/null +++ b/packages/server/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "server" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +server_fn = { version = "0.2.4", features = ["stable"] } + +# warp +warp = { version = "0.3.3", optional = true } + +# axum +axum = { version = "0.6.1", optional = true, features = ["ws"] } + +# salvo +salvo = { version = "0.37.7", optional = true, features = ["ws"] } +serde = "1.0.159" + +dioxus = { path = "../dioxus", version = "^0.3.0" } \ No newline at end of file diff --git a/packages/server/server_macro/Cargo.toml b/packages/server/server_macro/Cargo.toml new file mode 100644 index 000000000..6e8c69b46 --- /dev/null +++ b/packages/server/server_macro/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "server_macro" +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/server/server_macro/src/lib.rs b/packages/server/server_macro/src/lib.rs new file mode 100644 index 000000000..439a9e7b2 --- /dev/null +++ b/packages/server/server_macro/src/lib.rs @@ -0,0 +1,67 @@ +/// Declares that a function is a [server function](leptos_server). This means that +/// its body will only run on the server, i.e., when the `ssr` feature is enabled. +/// +/// If you call a server function from the client (i.e., when the `csr` or `hydrate` features +/// are enabled), it will instead make a network request to the server. +/// +/// You can specify one, two, or three arguments to the server function: +/// 1. **Required**: A type name that will be used to identify and register the server function +/// (e.g., `MyServerFn`). +/// 2. *Optional*: A URL prefix at which the function will be mounted when it’s registered +/// (e.g., `"/api"`). Defaults to `"/"`. +/// 3. *Optional*: either `"Cbor"` (specifying that it should use the binary `cbor` format for +/// serialization) or `"Url"` (specifying that it should be use a URL-encoded form-data string). +/// Defaults to `"Url"`. If you want to use this server function to power a `
` that will +/// work without WebAssembly, the encoding must be `"Url"`. +/// +/// The server function itself can take any number of arguments, each of which should be serializable +/// and deserializable with `serde`. Optionally, its first argument can be a Leptos [Scope](leptos_reactive::Scope), +/// which will be injected *on the server side.* This can be used to inject the raw HTTP request or other +/// server-side context into the server function. +/// +/// ```ignore +/// # use leptos::*; use serde::{Serialize, Deserialize}; +/// # #[derive(Serialize, Deserialize)] +/// # pub struct Post { } +/// #[server(ReadPosts, "/api")] +/// pub async fn read_posts(how_many: u8, query: String) -> Result, ServerFnError> { +/// // do some work on the server to access the database +/// todo!() +/// } +/// ``` +/// +/// Note the following: +/// - You must **register** the server function by calling `T::register()` somewhere in your main function. +/// - **Server functions must be `async`.** Even if the work being done inside the function body +/// can run synchronously on the server, from the client’s perspective it involves an asynchronous +/// function call. +/// - **Server functions must return `Result`.** Even if the work being done +/// inside the function body can’t fail, the processes of serialization/deserialization and the +/// network call are fallible. +/// - **Return types must be [Serializable](leptos_reactive::Serializable).** +/// This should be fairly obvious: we have to serialize arguments to send them to the server, and we +/// need to deserialize the result to return it to the client. +/// - **Arguments must be implement [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) +/// and [`DeserializeOwned`](https://docs.rs/serde/latest/serde/de/trait.DeserializeOwned.html).** +/// They are serialized as an `application/x-www-form-urlencoded` +/// form data using [`serde_urlencoded`](https://docs.rs/serde_urlencoded/latest/serde_urlencoded/) or as `application/cbor` +/// using [`cbor`](https://docs.rs/cbor/latest/cbor/). +/// - **The [Scope](leptos_reactive::Scope) comes from the server.** Optionally, the first argument of a server function +/// can be a Leptos [Scope](leptos_reactive::Scope). This scope can be used to inject dependencies like the HTTP request +/// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client. +#[proc_macro_attribute] +pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { + let context = ServerContext { + ty: syn::parse_quote!(Scope), + path: syn::parse_quote!(::leptos::Scope), + }; + match server_macro_impl( + args.into(), + s.into(), + Some(context), + Some(syn::parse_quote!(::leptos::server_fn)), + ) { + Err(e) => e.to_compile_error().into(), + Ok(s) => s.to_token_stream().into(), + } +} diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs new file mode 100644 index 000000000..646cde38e --- /dev/null +++ b/packages/server/src/adapters/axum_adapter.rs @@ -0,0 +1,23 @@ +use crate::{LiveViewError, LiveViewSocket}; +use axum::extract::ws::{Message, WebSocket}; +use futures_util::{SinkExt, StreamExt}; + +/// Convert a warp websocket into a LiveViewSocket +/// +/// This is required to launch a LiveView app using the warp web framework +pub fn axum_socket(ws: WebSocket) -> impl LiveViewSocket { + ws.map(transform_rx) + .with(transform_tx) + .sink_map_err(|_| LiveViewError::SendingFailed) +} + +fn transform_rx(message: Result) -> Result { + message + .map_err(|_| LiveViewError::SendingFailed)? + .into_text() + .map_err(|_| LiveViewError::SendingFailed) +} + +async fn transform_tx(message: String) -> Result { + Ok(Message::Text(message)) +} diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs new file mode 100644 index 000000000..2c8912a57 --- /dev/null +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -0,0 +1,25 @@ +use futures_util::{SinkExt, StreamExt}; +use salvo::ws::{Message, WebSocket}; + +use crate::{LiveViewError, LiveViewSocket}; + +/// Convert a salvo websocket into a LiveViewSocket +/// +/// This is required to launch a LiveView app using the warp web framework +pub fn salvo_socket(ws: WebSocket) -> impl LiveViewSocket { + ws.map(transform_rx) + .with(transform_tx) + .sink_map_err(|_| LiveViewError::SendingFailed) +} + +fn transform_rx(message: Result) -> Result { + let as_bytes = message.map_err(|_| LiveViewError::SendingFailed)?; + + let msg = String::from_utf8(as_bytes.into_bytes()).map_err(|_| LiveViewError::SendingFailed)?; + + Ok(msg) +} + +async fn transform_tx(message: String) -> Result { + Ok(Message::text(message)) +} diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs new file mode 100644 index 000000000..e5c821ce3 --- /dev/null +++ b/packages/server/src/adapters/warp_adapter.rs @@ -0,0 +1,28 @@ +use crate::{LiveViewError, LiveViewSocket}; +use futures_util::{SinkExt, StreamExt}; +use warp::ws::{Message, WebSocket}; + +/// Convert a warp websocket into a LiveViewSocket +/// +/// This is required to launch a LiveView app using the warp web framework +pub fn warp_socket(ws: WebSocket) -> impl LiveViewSocket { + ws.map(transform_rx) + .with(transform_tx) + .sink_map_err(|_| LiveViewError::SendingFailed) +} + +fn transform_rx(message: Result) -> Result { + // destructure the message into the buffer we got from warp + let msg = message + .map_err(|_| LiveViewError::SendingFailed)? + .into_bytes(); + + // transform it back into a string, saving us the allocation + let msg = String::from_utf8(msg).map_err(|_| LiveViewError::SendingFailed)?; + + Ok(msg) +} + +async fn transform_tx(message: String) -> Result { + Ok(Message::text(message)) +} diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs new file mode 100644 index 000000000..49b635fc2 --- /dev/null +++ b/packages/server/src/lib.rs @@ -0,0 +1,85 @@ +use dioxus::prelude::*; +use serde::{de::DeserializeOwned, Deserializer, Serialize, Serializer}; + +// We use deref specialization to make it possible to pass either a value that implements +pub trait SerializeToRemoteWrapper { + fn serialize_to_remote(&self, serializer: S) -> Result; +} + +impl SerializeToRemoteWrapper for &T { + fn serialize_to_remote( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> { + self.serialize(serializer) + } +} + +impl SerializeToRemoteWrapper for &mut &S { + fn serialize_to_remote( + &self, + serializer: S2, + ) -> Result<::Ok, ::Error> { + (**self).serialize_to_remote(serializer) + } +} + +pub trait SerializeToRemote { + fn serialize_to_remote(&self, serializer: S) -> Result; +} + +impl SerializeToRemote for UseState { + fn serialize_to_remote( + &self, + serializer: S2, + ) -> Result<::Ok, ::Error> { + self.current().serialize(serializer) + } +} + +// We use deref specialization to make it possible to pass either a value that implements +pub trait DeserializeOnRemoteWrapper { + type Output; + + fn deserialize_on_remote<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result; +} + +impl DeserializeOnRemoteWrapper for &T { + type Output = T; + + fn deserialize_on_remote<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result { + T::deserialize(deserializer) + } +} + +impl DeserializeOnRemoteWrapper for &mut &D { + type Output = D::Output; + + fn deserialize_on_remote<'a, D2: Deserializer<'a>>( + deserializer: D2, + ) -> Result { + D::deserialize_on_remote(deserializer) + } +} + +pub trait DeserializeOnRemote { + type Output; + + fn deserialize_on_remote<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result; +} + +impl DeserializeOnRemote for UseState { + type Output = D; + + fn deserialize_on_remote<'a, D2: Deserializer<'a>>( + deserializer: D2, + ) -> Result { + D::deserialize(deserializer) + } +} From 939e75541e9bfbe67f0188df556e256b3032e343 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 29 Mar 2023 20:20:26 -0500 Subject: [PATCH 313/516] initial axum implementation --- packages/server/Cargo.toml | 15 +- packages/server/src/adapters/axum_adapter.rs | 116 +++++++++++-- packages/server/src/adapters/mod.rs | 2 + packages/server/src/lib.rs | 173 ++++++++++--------- 4 files changed, 211 insertions(+), 95 deletions(-) create mode 100644 packages/server/src/adapters/mod.rs diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 13704574b..4e3028aaa 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -18,4 +18,17 @@ axum = { version = "0.6.1", optional = true, features = ["ws"] } salvo = { version = "0.37.7", optional = true, features = ["ws"] } serde = "1.0.159" -dioxus = { path = "../dioxus", version = "^0.3.0" } \ No newline at end of file +dioxus = { path = "../dioxus", version = "^0.3.0" } + +log = "0.4.17" +once_cell = "1.17.1" +thiserror = "1.0.40" +hyper = "0.14.25" +tokio = { version = "1.27.0", features = ["full"] } + +[features] +default = ["axum", "ssr"] +warp = ["dep:warp"] +axum = ["dep:axum"] +salvo = ["dep:salvo"] +ssr = ["server_fn/ssr"] diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 646cde38e..3fb0a1d4e 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -1,23 +1,105 @@ -use crate::{LiveViewError, LiveViewSocket}; -use axum::extract::ws::{Message, WebSocket}; -use futures_util::{SinkExt, StreamExt}; +use std::{error::Error, sync::Arc}; -/// Convert a warp websocket into a LiveViewSocket -/// -/// This is required to launch a LiveView app using the warp web framework -pub fn axum_socket(ws: WebSocket) -> impl LiveViewSocket { - ws.map(transform_rx) - .with(transform_tx) - .sink_map_err(|_| LiveViewError::SendingFailed) +use axum::{ + body::{self, Body, BoxBody, Full}, + http::{HeaderMap, Request, Response, StatusCode}, + response::IntoResponse, + routing::post, + Router, +}; +use server_fn::{Payload, ServerFunctionRegistry}; +use tokio::task::spawn_blocking; + +use crate::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}; + +trait DioxusRouterExt { + fn register_server_fns(self) -> Self; } -fn transform_rx(message: Result) -> Result { - message - .map_err(|_| LiveViewError::SendingFailed)? - .into_text() - .map_err(|_| LiveViewError::SendingFailed) +impl DioxusRouterExt for Router { + fn register_server_fns(self) -> Self { + let mut router = self; + for server_fn_path in DioxusServerFnRegistry::paths_registered() { + let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); + router = router.route( + server_fn_path, + post(move |headers: HeaderMap, body: Request| async move { + server_fn_handler(DioxusServerContext {}, func.clone(), headers, body).await + // todo!() + }), + ); + } + router + } } -async fn transform_tx(message: String) -> Result { - Ok(Message::Text(message)) +async fn server_fn_handler( + server_context: DioxusServerContext, + function: Arc, + headers: HeaderMap, + req: Request, +) -> impl IntoResponse { + let (_, body) = req.into_parts(); + let body = hyper::body::to_bytes(body).await; + let Ok(body)=body else { + return report_err(body.err().unwrap()); + }; + + // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime + let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); + spawn_blocking({ + move || { + tokio::runtime::Runtime::new() + .expect("couldn't spawn runtime") + .block_on(async { + let resp = match function(server_context, &body).await { + Ok(serialized) => { + // if this is Accept: application/json then send a serialized JSON response + let accept_header = + headers.get("Accept").and_then(|value| value.to_str().ok()); + let mut res = Response::builder(); + if accept_header == Some("application/json") + || accept_header + == Some( + "application/\ + x-www-form-urlencoded", + ) + || accept_header == Some("application/cbor") + { + res = res.status(StatusCode::OK); + } + + let resp = match serialized { + Payload::Binary(data) => res + .header("Content-Type", "application/cbor") + .body(body::boxed(Full::from(data))), + Payload::Url(data) => res + .header( + "Content-Type", + "application/\ + x-www-form-urlencoded", + ) + .body(body::boxed(data)), + Payload::Json(data) => res + .header("Content-Type", "application/json") + .body(body::boxed(data)), + }; + + resp.unwrap() + } + Err(e) => report_err(e), + }; + + resp_tx.send(resp).unwrap(); + }) + } + }); + resp_rx.await.unwrap() +} + +fn report_err(e: E) -> Response { + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(body::boxed(format!("Error: {}", e))) + .unwrap() } diff --git a/packages/server/src/adapters/mod.rs b/packages/server/src/adapters/mod.rs new file mode 100644 index 000000000..ce2670dde --- /dev/null +++ b/packages/server/src/adapters/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "axum")] +mod axum_adapter; diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 49b635fc2..c0ec07e06 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -1,85 +1,104 @@ -use dioxus::prelude::*; -use serde::{de::DeserializeOwned, Deserializer, Serialize, Serializer}; +mod adapters; -// We use deref specialization to make it possible to pass either a value that implements -pub trait SerializeToRemoteWrapper { - fn serialize_to_remote(&self, serializer: S) -> Result; -} +// #[server(ReadPosts, "api")] +// async fn testing(rx: i32) -> Result { +// Ok(0) +// } -impl SerializeToRemoteWrapper for &T { - fn serialize_to_remote( - &self, - serializer: S, - ) -> Result<::Ok, ::Error> { - self.serialize(serializer) +pub struct DioxusServerContext {} + +#[cfg(any(feature = "ssr", doc))] +type ServerFnTraitObj = server_fn::ServerFnTraitObj; + +#[cfg(any(feature = "ssr", doc))] +static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy< + std::sync::Arc< + std::sync::RwLock< + std::collections::HashMap<&'static str, std::sync::Arc>, + >, + >, +> = once_cell::sync::Lazy::new(Default::default); + +#[cfg(any(feature = "ssr", doc))] +/// The registry of all Dioxus server functions. +pub struct DioxusServerFnRegistry; + +#[cfg(any(feature = "ssr"))] +impl server_fn::ServerFunctionRegistry for DioxusServerFnRegistry { + type Error = ServerRegistrationFnError; + + fn register( + url: &'static str, + server_function: std::sync::Arc, + ) -> Result<(), Self::Error> { + // store it in the hashmap + let mut write = REGISTERED_SERVER_FUNCTIONS + .write() + .map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?; + let prev = write.insert(url, server_function); + + // if there was already a server function with this key, + // return Err + match prev { + Some(_) => Err(ServerRegistrationFnError::AlreadyRegistered(format!( + "There was already a server function registered at {:?}. \ + This can happen if you use the same server function name \ + in two different modules + on `stable` or in `release` mode.", + url + ))), + None => Ok(()), + } + } + + /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. + fn get(url: &str) -> Option> { + REGISTERED_SERVER_FUNCTIONS + .read() + .ok() + .and_then(|fns| fns.get(url).cloned()) + } + + /// Returns a list of all registered server functions. + fn paths_registered() -> Vec<&'static str> { + REGISTERED_SERVER_FUNCTIONS + .read() + .ok() + .map(|fns| fns.keys().cloned().collect()) + .unwrap_or_default() } } -impl SerializeToRemoteWrapper for &mut &S { - fn serialize_to_remote( - &self, - serializer: S2, - ) -> Result<::Ok, ::Error> { - (**self).serialize_to_remote(serializer) +#[cfg(any(feature = "ssr", doc))] +/// Errors that can occur when registering a server function. +#[derive(thiserror::Error, Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum ServerRegistrationFnError { + /// The server function is already registered. + #[error("The server function {0} is already registered")] + AlreadyRegistered(String), + /// The server function registry is poisoned. + #[error("The server function registry is poisoned: {0}")] + Poisoned(String), +} + +/// Defines a "server function." A server function can be called from the server or the client, +/// but the body of its code will only be run on the server, i.e., if a crate feature `ssr` is enabled. +/// +/// (This follows the same convention as the Dioxus framework's distinction between `ssr` for server-side rendering, +/// and `csr` and `hydrate` for client-side rendering and hydration, respectively.) +/// +/// Server functions are created using the `server` macro. +/// +/// The function should be registered by calling `ServerFn::register()`. The set of server functions +/// can be queried on the server for routing purposes by calling [server_fn_by_path]. +/// +/// Technically, the trait is implemented on a type that describes the server function's arguments. +pub trait ServerFn: server_fn::ServerFn { + /// Registers the server function, allowing the server to query it by URL. + #[cfg(any(feature = "ssr", doc))] + fn register() -> Result<(), server_fn::ServerFnError> { + Self::register_in::() } } -pub trait SerializeToRemote { - fn serialize_to_remote(&self, serializer: S) -> Result; -} - -impl SerializeToRemote for UseState { - fn serialize_to_remote( - &self, - serializer: S2, - ) -> Result<::Ok, ::Error> { - self.current().serialize(serializer) - } -} - -// We use deref specialization to make it possible to pass either a value that implements -pub trait DeserializeOnRemoteWrapper { - type Output; - - fn deserialize_on_remote<'a, D: Deserializer<'a>>( - deserializer: D, - ) -> Result; -} - -impl DeserializeOnRemoteWrapper for &T { - type Output = T; - - fn deserialize_on_remote<'a, D: Deserializer<'a>>( - deserializer: D, - ) -> Result { - T::deserialize(deserializer) - } -} - -impl DeserializeOnRemoteWrapper for &mut &D { - type Output = D::Output; - - fn deserialize_on_remote<'a, D2: Deserializer<'a>>( - deserializer: D2, - ) -> Result { - D::deserialize_on_remote(deserializer) - } -} - -pub trait DeserializeOnRemote { - type Output; - - fn deserialize_on_remote<'a, D: Deserializer<'a>>( - deserializer: D, - ) -> Result; -} - -impl DeserializeOnRemote for UseState { - type Output = D; - - fn deserialize_on_remote<'a, D2: Deserializer<'a>>( - deserializer: D2, - ) -> Result { - D::deserialize(deserializer) - } -} +impl ServerFn for T where T: server_fn::ServerFn {} From fdc8ebd1b196fddf5abd2cbc50cbd98c9fc0f912 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 10:34:13 -0500 Subject: [PATCH 314/516] create fullstack hello world example --- packages/server/.gitignore | 1 + packages/server/Cargo.toml | 23 ++- .../server/examples/hello-world/.gitignore | 2 + .../server/examples/hello-world/Cargo.toml | 20 +++ .../server/examples/hello-world/src/main.rs | 70 +++++++++ packages/server/server_macro/Cargo.toml | 6 + packages/server/server_macro/src/lib.rs | 10 +- packages/server/src/adapters/axum_adapter.rs | 49 +++++- packages/server/src/adapters/mod.rs | 2 +- packages/server/src/lib.rs | 148 ++++++------------ packages/server/src/server_fn.rs | 98 ++++++++++++ 11 files changed, 312 insertions(+), 117 deletions(-) create mode 100644 packages/server/.gitignore create mode 100644 packages/server/examples/hello-world/.gitignore create mode 100644 packages/server/examples/hello-world/Cargo.toml create mode 100644 packages/server/examples/hello-world/src/main.rs create mode 100644 packages/server/src/server_fn.rs diff --git a/packages/server/.gitignore b/packages/server/.gitignore new file mode 100644 index 000000000..1de565933 --- /dev/null +++ b/packages/server/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 4e3028aaa..af7e03859 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "server" +name = "dioxus-server" version = "0.1.0" edition = "2021" @@ -7,28 +7,37 @@ edition = "2021" [dependencies] server_fn = { version = "0.2.4", features = ["stable"] } +server_macro = { path = "server_macro" } # warp warp = { version = "0.3.3", optional = true } # axum axum = { version = "0.6.1", optional = true, features = ["ws"] } +tower-http = { version = "0.4.0", optional = true, features = ["fs"] } +hyper = { version = "0.14.25", optional = true } # salvo salvo = { version = "0.37.7", optional = true, features = ["ws"] } serde = "1.0.159" -dioxus = { path = "../dioxus", version = "^0.3.0" } +dioxus-core = { path = "../core", version = "^0.3.0" } +dioxus-ssr = { path = "../ssr", version = "^0.3.0", optional = true } log = "0.4.17" once_cell = "1.17.1" thiserror = "1.0.40" -hyper = "0.14.25" -tokio = { version = "1.27.0", features = ["full"] } +tokio = { version = "1.27.0", features = ["full"], optional = true } [features] -default = ["axum", "ssr"] +default = [] warp = ["dep:warp"] -axum = ["dep:axum"] +axum = ["dep:axum", "tower-http", "hyper"] salvo = ["dep:salvo"] -ssr = ["server_fn/ssr"] +ssr = ["server_fn/ssr", "tokio", "dioxus-ssr"] + +[workspace] +members = [ + "server_macro", + "examples/hello-world", +] \ No newline at end of file diff --git a/packages/server/examples/hello-world/.gitignore b/packages/server/examples/hello-world/.gitignore new file mode 100644 index 000000000..6047329c6 --- /dev/null +++ b/packages/server/examples/hello-world/.gitignore @@ -0,0 +1,2 @@ +dist +target \ No newline at end of file diff --git a/packages/server/examples/hello-world/Cargo.toml b/packages/server/examples/hello-world/Cargo.toml new file mode 100644 index 000000000..846c03ea4 --- /dev/null +++ b/packages/server/examples/hello-world/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "hello-world" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } +dioxus = { path = "../../../dioxus" } +dioxus-server = { path = "../../" } +axum = { version = "0.6.12", optional = true } +tokio = { version = "1.27.0", features = ["full"], optional = true } +serde = "1.0.159" +tracing-subscriber = "0.3.16" +tracing = "0.1.37" + +[features] +ssr = ["axum", "tokio", "dioxus-server/ssr", "dioxus-server/axum"] +web = ["dioxus-web"] diff --git a/packages/server/examples/hello-world/src/main.rs b/packages/server/examples/hello-world/src/main.rs new file mode 100644 index 000000000..9b9084963 --- /dev/null +++ b/packages/server/examples/hello-world/src/main.rs @@ -0,0 +1,70 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_server::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + PostServerData::register().unwrap(); + GetServerData::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + .serve_dioxus_application( + "Hello, world!", + "hello-world", + None, + None, + "", + app, + ) + .into_make_service(), + ) + .await + .unwrap(); + }); + } +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + let text = use_state(cx, || "...".to_string()); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![text]; + async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + } + }, + "Run a server function" + } + "Server said: {text}" + }) +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/server/server_macro/Cargo.toml b/packages/server/server_macro/Cargo.toml index 6e8c69b46..cb285f517 100644 --- a/packages/server/server_macro/Cargo.toml +++ b/packages/server/server_macro/Cargo.toml @@ -6,3 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +quote = "1.0.26" +server_fn_macro = { version = "0.2.4", features = ["stable"] } +syn = { version = "1", features = ["full"] } + +[lib] +proc-macro = true diff --git a/packages/server/server_macro/src/lib.rs b/packages/server/server_macro/src/lib.rs index 439a9e7b2..f2a2e1665 100644 --- a/packages/server/server_macro/src/lib.rs +++ b/packages/server/server_macro/src/lib.rs @@ -1,3 +1,7 @@ +use proc_macro::TokenStream; +use quote::ToTokens; +use server_fn_macro::*; + /// Declares that a function is a [server function](leptos_server). This means that /// its body will only run on the server, i.e., when the `ssr` feature is enabled. /// @@ -52,14 +56,14 @@ #[proc_macro_attribute] pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { let context = ServerContext { - ty: syn::parse_quote!(Scope), - path: syn::parse_quote!(::leptos::Scope), + ty: syn::parse_quote!(DioxusServerContext), + path: syn::parse_quote!(::dioxus_server::prelude::DioxusServerContext), }; match server_macro_impl( args.into(), s.into(), Some(context), - Some(syn::parse_quote!(::leptos::server_fn)), + Some(syn::parse_quote!(::dioxus_server::prelude::server_fn)), ) { Err(e) => e.to_compile_error().into(), Ok(s) => s.to_token_stream().into(), diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 3fb0a1d4e..70513bdb3 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -4,33 +4,68 @@ use axum::{ body::{self, Body, BoxBody, Full}, http::{HeaderMap, Request, Response, StatusCode}, response::IntoResponse, - routing::post, + routing::{get, post}, Router, }; +use dioxus_core::Component; use server_fn::{Payload, ServerFunctionRegistry}; use tokio::task::spawn_blocking; -use crate::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}; +use crate::{ + dioxus_ssr_html, + server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, +}; -trait DioxusRouterExt { - fn register_server_fns(self) -> Self; +pub trait DioxusRouterExt { + fn register_server_fns(self, server_fn_route: &'static str) -> Self; + fn serve_dioxus_application( + self, + title: &'static str, + application_name: &'static str, + base_path: Option<&'static str>, + head: Option<&'static str>, + server_fn_route: &'static str, + app: Component, + ) -> Self; } impl DioxusRouterExt for Router { - fn register_server_fns(self) -> Self { + fn register_server_fns(self, server_fn_route: &'static str) -> Self { let mut router = self; for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); + let full_route = format!("{server_fn_route}/{server_fn_path}"); router = router.route( - server_fn_path, + &full_route, post(move |headers: HeaderMap, body: Request| async move { server_fn_handler(DioxusServerContext {}, func.clone(), headers, body).await - // todo!() }), ); } router } + + fn serve_dioxus_application( + self, + title: &'static str, + application_name: &'static str, + base_path: Option<&'static str>, + head: Option<&'static str>, + server_fn_route: &'static str, + app: Component, + ) -> Self { + use tower_http::services::ServeDir; + + // Serve the dist folder and the index.html file + let serve_dir = ServeDir::new("dist"); + + self.register_server_fns(server_fn_route) + .nest_service("/", serve_dir) + .fallback_service(get(move || { + let rendered = dioxus_ssr_html(title, application_name, base_path, head, app); + async move { Full::from(rendered) } + })) + } } async fn server_fn_handler( diff --git a/packages/server/src/adapters/mod.rs b/packages/server/src/adapters/mod.rs index ce2670dde..c30365989 100644 --- a/packages/server/src/adapters/mod.rs +++ b/packages/server/src/adapters/mod.rs @@ -1,2 +1,2 @@ #[cfg(feature = "axum")] -mod axum_adapter; +pub mod axum_adapter; diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index c0ec07e06..c78f3471b 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -1,104 +1,54 @@ +#[allow(unused)] +use dioxus_core::prelude::*; + mod adapters; +mod server_fn; -// #[server(ReadPosts, "api")] -// async fn testing(rx: i32) -> Result { -// Ok(0) -// } - -pub struct DioxusServerContext {} - -#[cfg(any(feature = "ssr", doc))] -type ServerFnTraitObj = server_fn::ServerFnTraitObj; - -#[cfg(any(feature = "ssr", doc))] -static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy< - std::sync::Arc< - std::sync::RwLock< - std::collections::HashMap<&'static str, std::sync::Arc>, - >, - >, -> = once_cell::sync::Lazy::new(Default::default); - -#[cfg(any(feature = "ssr", doc))] -/// The registry of all Dioxus server functions. -pub struct DioxusServerFnRegistry; - -#[cfg(any(feature = "ssr"))] -impl server_fn::ServerFunctionRegistry for DioxusServerFnRegistry { - type Error = ServerRegistrationFnError; - - fn register( - url: &'static str, - server_function: std::sync::Arc, - ) -> Result<(), Self::Error> { - // store it in the hashmap - let mut write = REGISTERED_SERVER_FUNCTIONS - .write() - .map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?; - let prev = write.insert(url, server_function); - - // if there was already a server function with this key, - // return Err - match prev { - Some(_) => Err(ServerRegistrationFnError::AlreadyRegistered(format!( - "There was already a server function registered at {:?}. \ - This can happen if you use the same server function name \ - in two different modules - on `stable` or in `release` mode.", - url - ))), - None => Ok(()), - } - } - - /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. - fn get(url: &str) -> Option> { - REGISTERED_SERVER_FUNCTIONS - .read() - .ok() - .and_then(|fns| fns.get(url).cloned()) - } - - /// Returns a list of all registered server functions. - fn paths_registered() -> Vec<&'static str> { - REGISTERED_SERVER_FUNCTIONS - .read() - .ok() - .map(|fns| fns.keys().cloned().collect()) - .unwrap_or_default() - } +pub mod prelude { + #[cfg(feature = "axum")] + pub use crate::adapters::axum_adapter::*; + pub use crate::server_fn::{DioxusServerContext, ServerFn}; + pub use server_fn::{self, ServerFn as _, ServerFnError}; + pub use server_macro::*; } -#[cfg(any(feature = "ssr", doc))] -/// Errors that can occur when registering a server function. -#[derive(thiserror::Error, Debug, Clone, serde::Serialize, serde::Deserialize)] -pub enum ServerRegistrationFnError { - /// The server function is already registered. - #[error("The server function {0} is already registered")] - AlreadyRegistered(String), - /// The server function registry is poisoned. - #[error("The server function registry is poisoned: {0}")] - Poisoned(String), +#[cfg(feature = "ssr")] +fn dioxus_ssr_html( + title: &str, + application_name: &str, + base_path: Option<&str>, + head: Option<&str>, + app: Component, +) -> String { + let mut vdom = VirtualDom::new(app); + let _ = vdom.rebuild(); + let renderered = dioxus_ssr::pre_render(&vdom); + let base_path = base_path.unwrap_or("."); + let head = head.unwrap_or_default(); + format!( + r#" + + + + {title} + + + + {head} + + +
+ {renderered} +
+ + +"# + ) } - -/// Defines a "server function." A server function can be called from the server or the client, -/// but the body of its code will only be run on the server, i.e., if a crate feature `ssr` is enabled. -/// -/// (This follows the same convention as the Dioxus framework's distinction between `ssr` for server-side rendering, -/// and `csr` and `hydrate` for client-side rendering and hydration, respectively.) -/// -/// Server functions are created using the `server` macro. -/// -/// The function should be registered by calling `ServerFn::register()`. The set of server functions -/// can be queried on the server for routing purposes by calling [server_fn_by_path]. -/// -/// Technically, the trait is implemented on a type that describes the server function's arguments. -pub trait ServerFn: server_fn::ServerFn { - /// Registers the server function, allowing the server to query it by URL. - #[cfg(any(feature = "ssr", doc))] - fn register() -> Result<(), server_fn::ServerFnError> { - Self::register_in::() - } -} - -impl ServerFn for T where T: server_fn::ServerFn {} diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs new file mode 100644 index 000000000..28c7cd9b6 --- /dev/null +++ b/packages/server/src/server_fn.rs @@ -0,0 +1,98 @@ +pub struct DioxusServerContext {} + +#[cfg(any(feature = "ssr", doc))] +pub type ServerFnTraitObj = server_fn::ServerFnTraitObj; + +#[cfg(any(feature = "ssr", doc))] +#[allow(clippy::type_complexity)] +static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy< + std::sync::Arc< + std::sync::RwLock< + std::collections::HashMap<&'static str, std::sync::Arc>, + >, + >, +> = once_cell::sync::Lazy::new(Default::default); + +#[cfg(any(feature = "ssr", doc))] +/// The registry of all Dioxus server functions. +pub struct DioxusServerFnRegistry; + +#[cfg(any(feature = "ssr"))] +impl server_fn::ServerFunctionRegistry for DioxusServerFnRegistry { + type Error = ServerRegistrationFnError; + + fn register( + url: &'static str, + server_function: std::sync::Arc, + ) -> Result<(), Self::Error> { + // store it in the hashmap + let mut write = REGISTERED_SERVER_FUNCTIONS + .write() + .map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?; + let prev = write.insert(url, server_function); + + // if there was already a server function with this key, + // return Err + match prev { + Some(_) => Err(ServerRegistrationFnError::AlreadyRegistered(format!( + "There was already a server function registered at {:?}. \ + This can happen if you use the same server function name \ + in two different modules + on `stable` or in `release` mode.", + url + ))), + None => Ok(()), + } + } + + /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. + fn get(url: &str) -> Option> { + REGISTERED_SERVER_FUNCTIONS + .read() + .ok() + .and_then(|fns| fns.get(url).cloned()) + } + + /// Returns a list of all registered server functions. + fn paths_registered() -> Vec<&'static str> { + REGISTERED_SERVER_FUNCTIONS + .read() + .ok() + .map(|fns| fns.keys().cloned().collect()) + .unwrap_or_default() + } +} + +#[cfg(any(feature = "ssr", doc))] +/// Errors that can occur when registering a server function. +#[derive(thiserror::Error, Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum ServerRegistrationFnError { + /// The server function is already registered. + #[error("The server function {0} is already registered")] + AlreadyRegistered(String), + /// The server function registry is poisoned. + #[error("The server function registry is poisoned: {0}")] + Poisoned(String), +} + +/// Defines a "server function." A server function can be called from the server or the client, +/// but the body of its code will only be run on the server, i.e., if a crate feature `ssr` is enabled. +/// +/// (This follows the same convention as the Dioxus framework's distinction between `ssr` for server-side rendering, +/// and `csr` and `hydrate` for client-side rendering and hydration, respectively.) +/// +/// Server functions are created using the `server` macro. +/// +/// The function should be registered by calling `ServerFn::register()`. The set of server functions +/// can be queried on the server for routing purposes by calling [server_fn_by_path]. +/// +/// Technically, the trait is implemented on a type that describes the server function's arguments. +pub trait ServerFn: server_fn::ServerFn { + /// Registers the server function, allowing the server to query it by URL. + #[cfg(any(feature = "ssr", doc))] + fn register() -> Result<(), server_fn::ServerFnError> { + Self::register_in::() + } +} + +impl ServerFn for T where T: server_fn::ServerFn {} From 1d395d572f2f642174378cf2a1e3e99695702726 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 10:38:32 -0500 Subject: [PATCH 315/516] fix workspace build --- Cargo.toml | 2 ++ packages/server/Cargo.toml | 8 +------- packages/server/{server_macro => server-macro}/Cargo.toml | 0 packages/server/{server_macro => server-macro}/src/lib.rs | 0 4 files changed, 3 insertions(+), 7 deletions(-) rename packages/server/{server_macro => server-macro}/Cargo.toml (100%) rename packages/server/{server_macro => server-macro}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 3b36bb5e3..8389566ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ members = [ "packages/signals", "packages/hot-reload", "packages/server", + "packages/server/server-macro", + "packages/server/examples/hello-world", "docs/guide", ] diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index af7e03859..57ad88da1 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] server_fn = { version = "0.2.4", features = ["stable"] } -server_macro = { path = "server_macro" } +server_macro = { path = "server-macro" } # warp warp = { version = "0.3.3", optional = true } @@ -35,9 +35,3 @@ warp = ["dep:warp"] axum = ["dep:axum", "tower-http", "hyper"] salvo = ["dep:salvo"] ssr = ["server_fn/ssr", "tokio", "dioxus-ssr"] - -[workspace] -members = [ - "server_macro", - "examples/hello-world", -] \ No newline at end of file diff --git a/packages/server/server_macro/Cargo.toml b/packages/server/server-macro/Cargo.toml similarity index 100% rename from packages/server/server_macro/Cargo.toml rename to packages/server/server-macro/Cargo.toml diff --git a/packages/server/server_macro/src/lib.rs b/packages/server/server-macro/src/lib.rs similarity index 100% rename from packages/server/server_macro/src/lib.rs rename to packages/server/server-macro/src/lib.rs From 2c787f85f47deeda4bbfa81802592989e742a5f4 Mon Sep 17 00:00:00 2001 From: Ben Schulz Date: Thu, 30 Mar 2023 18:01:18 +0200 Subject: [PATCH 316/516] =?UTF-8?q?Add=20proxy=20routes=20in=20startup=5Fd?= =?UTF-8?q?efault(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index e78a8fb84..42622466b 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -447,7 +447,9 @@ pub async fn startup_default(ip: String, port: u16, config: CrateConfig) -> Resu .service(ServeDir::new(config.crate_dir.join(&dist_path))); let mut router = Router::new().route("/_dioxus/ws", get(ws_handler)); - + for proxy_config in config.dioxus_config.web.proxy.unwrap_or_default() { + router = proxy::add_proxy(router, &proxy_config)?; + } router = router .fallback( get_service(file_service).handle_error(|error: std::io::Error| async move { From 1be48c4aa8cdb45f5a35014e59c4f62b8d21a906 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 11:03:07 -0500 Subject: [PATCH 317/516] create a serveconfig builder --- .../server/examples/hello-world/src/main.rs | 7 +-- packages/server/src/adapters/axum_adapter.rs | 26 ++-------- packages/server/src/lib.rs | 33 ++++++++----- packages/server/src/serve.rs | 47 +++++++++++++++++++ 4 files changed, 74 insertions(+), 39 deletions(-) create mode 100644 packages/server/src/serve.rs diff --git a/packages/server/examples/hello-world/src/main.rs b/packages/server/examples/hello-world/src/main.rs index 9b9084963..9cafe93b5 100644 --- a/packages/server/examples/hello-world/src/main.rs +++ b/packages/server/examples/hello-world/src/main.rs @@ -17,12 +17,7 @@ fn main() { .serve( axum::Router::new() .serve_dioxus_application( - "Hello, world!", - "hello-world", - None, - None, - "", - app, + ServeConfig::new(app).head(r#"Hello World!"#), ) .into_make_service(), ) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 70513bdb3..649629f61 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -7,26 +7,18 @@ use axum::{ routing::{get, post}, Router, }; -use dioxus_core::Component; use server_fn::{Payload, ServerFunctionRegistry}; use tokio::task::spawn_blocking; use crate::{ dioxus_ssr_html, + serve::ServeConfig, server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, }; pub trait DioxusRouterExt { fn register_server_fns(self, server_fn_route: &'static str) -> Self; - fn serve_dioxus_application( - self, - title: &'static str, - application_name: &'static str, - base_path: Option<&'static str>, - head: Option<&'static str>, - server_fn_route: &'static str, - app: Component, - ) -> Self; + fn serve_dioxus_application(self, cfg: ServeConfig) -> Self; } impl DioxusRouterExt for Router { @@ -45,24 +37,16 @@ impl DioxusRouterExt for Router { router } - fn serve_dioxus_application( - self, - title: &'static str, - application_name: &'static str, - base_path: Option<&'static str>, - head: Option<&'static str>, - server_fn_route: &'static str, - app: Component, - ) -> Self { + fn serve_dioxus_application(self, cfg: ServeConfig) -> Self { use tower_http::services::ServeDir; // Serve the dist folder and the index.html file let serve_dir = ServeDir::new("dist"); - self.register_server_fns(server_fn_route) + self.register_server_fns(cfg.server_fn_route.unwrap_or_default()) .nest_service("/", serve_dir) .fallback_service(get(move || { - let rendered = dioxus_ssr_html(title, application_name, base_path, head, app); + let rendered = dioxus_ssr_html(cfg); async move { Full::from(rendered) } })) } diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index c78f3471b..e14a445b5 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -2,38 +2,47 @@ use dioxus_core::prelude::*; mod adapters; +mod serve; mod server_fn; pub mod prelude { #[cfg(feature = "axum")] pub use crate::adapters::axum_adapter::*; + pub use crate::serve::ServeConfig; pub use crate::server_fn::{DioxusServerContext, ServerFn}; pub use server_fn::{self, ServerFn as _, ServerFnError}; pub use server_macro::*; } #[cfg(feature = "ssr")] -fn dioxus_ssr_html( - title: &str, - application_name: &str, - base_path: Option<&str>, - head: Option<&str>, - app: Component, -) -> String { +fn dioxus_ssr_html(cfg: serve::ServeConfig) -> String { + use prelude::ServeConfig; + + let ServeConfig { + app, + application_name, + base_path, + head, + .. + } = cfg; + + let application_name = application_name.unwrap_or("dioxus"); + let mut vdom = VirtualDom::new(app); let _ = vdom.rebuild(); let renderered = dioxus_ssr::pre_render(&vdom); let base_path = base_path.unwrap_or("."); - let head = head.unwrap_or_default(); + let head = head.unwrap_or( + r#"Dioxus Application + + + "#, + ); format!( r#" - {title} - - - {head} diff --git a/packages/server/src/serve.rs b/packages/server/src/serve.rs new file mode 100644 index 000000000..7dc1eaaac --- /dev/null +++ b/packages/server/src/serve.rs @@ -0,0 +1,47 @@ +use dioxus_core::Component; + +#[derive(Clone)] +pub struct ServeConfig { + pub(crate) app: Component, + pub(crate) application_name: Option<&'static str>, + pub(crate) server_fn_route: Option<&'static str>, + pub(crate) base_path: Option<&'static str>, + pub(crate) head: Option<&'static str>, +} + +impl ServeConfig { + /// Create a new ServeConfig + pub fn new(app: Component) -> Self { + Self { + app, + application_name: None, + server_fn_route: None, + base_path: None, + head: None, + } + } + + /// Set the application name matching the name in the Dioxus.toml file used to build the application + pub fn application_name(mut self, application_name: &'static str) -> Self { + self.application_name = Some(application_name); + self + } + + /// Set the base route all server functions will be served under + pub fn server_fn_route(mut self, server_fn_route: &'static str) -> Self { + self.server_fn_route = Some(server_fn_route); + self + } + + /// Set the path the WASM application will be served under + pub fn base_path(mut self, base_path: &'static str) -> Self { + self.base_path = Some(base_path); + self + } + + /// Set the head content to be included in the HTML document served + pub fn head(mut self, head: &'static str) -> Self { + self.head = Some(head); + self + } +} From 39a5fbf26861c10be16abeb403378c7136137f24 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 15:58:03 -0500 Subject: [PATCH 318/516] add build instuctions --- packages/server/examples/hello-world/src/main.rs | 7 +++++++ packages/server/src/adapters/axum_adapter.rs | 4 ++-- packages/server/src/lib.rs | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/server/examples/hello-world/src/main.rs b/packages/server/examples/hello-world/src/main.rs index 9cafe93b5..2fd6b1bde 100644 --- a/packages/server/examples/hello-world/src/main.rs +++ b/packages/server/examples/hello-world/src/main.rs @@ -1,3 +1,10 @@ +//! Run with: +//! +//! ```sh +//! dioxus build --features web +//! cargo run --features ssr +//! ``` + #![allow(non_snake_case)] use dioxus::prelude::*; use dioxus_server::prelude::*; diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 649629f61..b7cc85e1c 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -44,11 +44,11 @@ impl DioxusRouterExt for Router { let serve_dir = ServeDir::new("dist"); self.register_server_fns(cfg.server_fn_route.unwrap_or_default()) - .nest_service("/", serve_dir) - .fallback_service(get(move || { + .route("/", get(move || { let rendered = dioxus_ssr_html(cfg); async move { Full::from(rendered) } })) + .fallback_service( serve_dir) } } diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index e14a445b5..98261d1d4 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -2,12 +2,14 @@ use dioxus_core::prelude::*; mod adapters; +#[cfg(feature = "ssr")] mod serve; mod server_fn; pub mod prelude { #[cfg(feature = "axum")] pub use crate::adapters::axum_adapter::*; + #[cfg(feature = "ssr")] pub use crate::serve::ServeConfig; pub use crate::server_fn::{DioxusServerContext, ServerFn}; pub use server_fn::{self, ServerFn as _, ServerFnError}; From f618da7311df3766fa96faf03485853c6d4cd716 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 19:42:46 -0500 Subject: [PATCH 319/516] allow passing props to the rendered component --- Cargo.toml | 3 ++- packages/server/Cargo.toml | 6 +++--- packages/server/src/adapters/mod.rs | 2 ++ packages/server/src/lib.rs | 7 +++++-- packages/server/src/serve.rs | 10 ++++++---- packages/server/src/server_fn.rs | 1 + 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8389566ee..bf2b4b8c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ members = [ "packages/hot-reload", "packages/server", "packages/server/server-macro", - "packages/server/examples/hello-world", + "packages/server/examples/axum-hello-world", + "packages/server/examples/salvo-hello-world", "docs/guide", ] diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 57ad88da1..fc5f33c7f 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -13,12 +13,12 @@ server_macro = { path = "server-macro" } warp = { version = "0.3.3", optional = true } # axum -axum = { version = "0.6.1", optional = true, features = ["ws"] } +axum = { version = "0.6.1", optional = true } tower-http = { version = "0.4.0", optional = true, features = ["fs"] } hyper = { version = "0.14.25", optional = true } # salvo -salvo = { version = "0.37.7", optional = true, features = ["ws"] } +salvo = { version = "0.37.7", optional = true, features = ["serve-static"] } serde = "1.0.159" dioxus-core = { path = "../core", version = "^0.3.0" } @@ -33,5 +33,5 @@ tokio = { version = "1.27.0", features = ["full"], optional = true } default = [] warp = ["dep:warp"] axum = ["dep:axum", "tower-http", "hyper"] -salvo = ["dep:salvo"] +salvo = ["dep:salvo", "hyper"] ssr = ["server_fn/ssr", "tokio", "dioxus-ssr"] diff --git a/packages/server/src/adapters/mod.rs b/packages/server/src/adapters/mod.rs index c30365989..41bb4a403 100644 --- a/packages/server/src/adapters/mod.rs +++ b/packages/server/src/adapters/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "axum")] pub mod axum_adapter; +#[cfg(feature = "salvo")] +pub mod salvo_adapter; diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 98261d1d4..e7559363a 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -9,6 +9,8 @@ mod server_fn; pub mod prelude { #[cfg(feature = "axum")] pub use crate::adapters::axum_adapter::*; + #[cfg(feature = "salvo")] + pub use crate::adapters::salvo_adapter::*; #[cfg(feature = "ssr")] pub use crate::serve::ServeConfig; pub use crate::server_fn::{DioxusServerContext, ServerFn}; @@ -17,7 +19,7 @@ pub mod prelude { } #[cfg(feature = "ssr")] -fn dioxus_ssr_html(cfg: serve::ServeConfig) -> String { +fn dioxus_ssr_html(cfg: &serve::ServeConfig

) -> String { use prelude::ServeConfig; let ServeConfig { @@ -25,12 +27,13 @@ fn dioxus_ssr_html(cfg: serve::ServeConfig) -> String { application_name, base_path, head, + props, .. } = cfg; let application_name = application_name.unwrap_or("dioxus"); - let mut vdom = VirtualDom::new(app); + let mut vdom = VirtualDom::new_with_props(*app, props.clone()); let _ = vdom.rebuild(); let renderered = dioxus_ssr::pre_render(&vdom); let base_path = base_path.unwrap_or("."); diff --git a/packages/server/src/serve.rs b/packages/server/src/serve.rs index 7dc1eaaac..9cca17a6b 100644 --- a/packages/server/src/serve.rs +++ b/packages/server/src/serve.rs @@ -1,19 +1,21 @@ use dioxus_core::Component; #[derive(Clone)] -pub struct ServeConfig { - pub(crate) app: Component, +pub struct ServeConfig { + pub(crate) app: Component

, + pub(crate) props: P, pub(crate) application_name: Option<&'static str>, pub(crate) server_fn_route: Option<&'static str>, pub(crate) base_path: Option<&'static str>, pub(crate) head: Option<&'static str>, } -impl ServeConfig { +impl ServeConfig

{ /// Create a new ServeConfig - pub fn new(app: Component) -> Self { + pub fn new(app: Component

, props: P) -> Self { Self { app, + props, application_name: None, server_fn_route: None, base_path: None, diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs index 28c7cd9b6..2f000adda 100644 --- a/packages/server/src/server_fn.rs +++ b/packages/server/src/server_fn.rs @@ -1,3 +1,4 @@ +#[derive(Clone)] pub struct DioxusServerContext {} #[cfg(any(feature = "ssr", doc))] From 83d513ef362f6cc17bd18d4c53bd9db7ebea3450 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 19:42:53 -0500 Subject: [PATCH 320/516] add salvo intigration --- .../.gitignore | 0 .../Cargo.toml | 2 +- .../src/main.rs | 4 +- .../examples/salvo-hello-world/.gitignore | 2 + .../examples/salvo-hello-world/Cargo.toml | 20 +++ .../examples/salvo-hello-world/src/main.rs | 68 ++++++++ packages/server/src/adapters/salvo_adapter.rs | 161 ++++++++++++++++-- 7 files changed, 238 insertions(+), 19 deletions(-) rename packages/server/examples/{hello-world => axum-hello-world}/.gitignore (100%) rename packages/server/examples/{hello-world => axum-hello-world}/Cargo.toml (95%) rename packages/server/examples/{hello-world => axum-hello-world}/src/main.rs (95%) create mode 100644 packages/server/examples/salvo-hello-world/.gitignore create mode 100644 packages/server/examples/salvo-hello-world/Cargo.toml create mode 100644 packages/server/examples/salvo-hello-world/src/main.rs diff --git a/packages/server/examples/hello-world/.gitignore b/packages/server/examples/axum-hello-world/.gitignore similarity index 100% rename from packages/server/examples/hello-world/.gitignore rename to packages/server/examples/axum-hello-world/.gitignore diff --git a/packages/server/examples/hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml similarity index 95% rename from packages/server/examples/hello-world/Cargo.toml rename to packages/server/examples/axum-hello-world/Cargo.toml index 846c03ea4..54aaf17d3 100644 --- a/packages/server/examples/hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "hello-world" +name = "axum-hello-world" version = "0.1.0" edition = "2021" diff --git a/packages/server/examples/hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs similarity index 95% rename from packages/server/examples/hello-world/src/main.rs rename to packages/server/examples/axum-hello-world/src/main.rs index 2fd6b1bde..4d4ab5e78 100644 --- a/packages/server/examples/hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -1,5 +1,5 @@ //! Run with: -//! +//! //! ```sh //! dioxus build --features web //! cargo run --features ssr @@ -24,7 +24,7 @@ fn main() { .serve( axum::Router::new() .serve_dioxus_application( - ServeConfig::new(app).head(r#"Hello World!"#), + ServeConfig::new(app, ()).head(r#"Hello World!"#), ) .into_make_service(), ) diff --git a/packages/server/examples/salvo-hello-world/.gitignore b/packages/server/examples/salvo-hello-world/.gitignore new file mode 100644 index 000000000..6047329c6 --- /dev/null +++ b/packages/server/examples/salvo-hello-world/.gitignore @@ -0,0 +1,2 @@ +dist +target \ No newline at end of file diff --git a/packages/server/examples/salvo-hello-world/Cargo.toml b/packages/server/examples/salvo-hello-world/Cargo.toml new file mode 100644 index 000000000..1ebe56dfc --- /dev/null +++ b/packages/server/examples/salvo-hello-world/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "salvo-hello-world" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } +dioxus = { path = "../../../dioxus" } +dioxus-server = { path = "../../" } +tokio = { version = "1.27.0", features = ["full"], optional = true } +serde = "1.0.159" +tracing-subscriber = "0.3.16" +tracing = "0.1.37" +salvo = { version = "0.37.9", optional = true } + +[features] +ssr = ["salvo", "tokio", "dioxus-server/ssr", "dioxus-server/salvo"] +web = ["dioxus-web"] diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs new file mode 100644 index 000000000..cd8b80c51 --- /dev/null +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -0,0 +1,68 @@ +//! Run with: +//! +//! ```sh +//! dioxus build --features web +//! cargo run --features ssr +//! ``` + +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_server::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + use salvo::prelude::*; + PostServerData::register().unwrap(); + GetServerData::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let router = Router::new().serve_dioxus_application( + ServeConfig::new(app, ()).head(r#"Hello World!"#), + ); + Server::new(TcpListener::bind("127.0.0.1:8080")) + .serve(router) + .await; + }); + } +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + let text = use_state(cx, || "...".to_string()); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![text]; + async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + } + }, + "Run a server function" + } + "Server said: {text}" + }) +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 2c8912a57..20edc9364 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -1,25 +1,154 @@ -use futures_util::{SinkExt, StreamExt}; -use salvo::ws::{Message, WebSocket}; +use std::{error::Error, sync::Arc}; -use crate::{LiveViewError, LiveViewSocket}; +use hyper::{http::HeaderValue, StatusCode}; +use salvo::{ + async_trait, handler, serve_static::StaticDir, Depot, FlowCtrl, Handler, Request, Response, + Router, +}; +use server_fn::{Payload, ServerFunctionRegistry}; +use tokio::task::spawn_blocking; -/// Convert a salvo websocket into a LiveViewSocket -/// -/// This is required to launch a LiveView app using the warp web framework -pub fn salvo_socket(ws: WebSocket) -> impl LiveViewSocket { - ws.map(transform_rx) - .with(transform_tx) - .sink_map_err(|_| LiveViewError::SendingFailed) +use crate::{ + dioxus_ssr_html, + serve::ServeConfig, + server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, +}; + +pub trait DioxusRouterExt { + fn register_server_fns(self, server_fn_route: &'static str) -> Self; + fn serve_dioxus_application( + self, + cfg: ServeConfig

, + ) -> Self; } -fn transform_rx(message: Result) -> Result { - let as_bytes = message.map_err(|_| LiveViewError::SendingFailed)?; +impl DioxusRouterExt for Router { + fn register_server_fns(self, server_fn_route: &'static str) -> Self { + let mut router = self; + for server_fn_path in DioxusServerFnRegistry::paths_registered() { + let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); + let full_route = format!("{server_fn_route}/{server_fn_path}"); + router = router.push(Router::with_path(&full_route).post(ServerFnHandler { + server_context: DioxusServerContext {}, + function: func, + })); + } + router + } - let msg = String::from_utf8(as_bytes.into_bytes()).map_err(|_| LiveViewError::SendingFailed)?; + fn serve_dioxus_application( + self, + cfg: ServeConfig

, + ) -> Self { + // Serve the dist folder and the index.html file + let serve_dir = StaticDir::new(["dist"]); - Ok(msg) + self.register_server_fns(cfg.server_fn_route.unwrap_or_default()) + .push(Router::with_path("/").get(SSRHandler { cfg })) + .push(Router::with_path("<**path>").get(serve_dir)) + } } -async fn transform_tx(message: String) -> Result { - Ok(Message::text(message)) +struct SSRHandler { + cfg: ServeConfig

, +} + +#[async_trait] +impl Handler for SSRHandler

{ + async fn handle( + &self, + _req: &mut Request, + _depot: &mut Depot, + res: &mut Response, + _flow: &mut FlowCtrl, + ) { + res.write_body(dioxus_ssr_html(&self.cfg)).unwrap(); + } +} + +struct ServerFnHandler { + server_context: DioxusServerContext, + function: Arc, +} + +#[handler] +impl ServerFnHandler { + async fn handle(&self, req: &mut Request, _depot: &mut Depot, res: &mut Response) { + let Self { + server_context, + function, + } = self; + + let body = hyper::body::to_bytes(req.body_mut().unwrap()).await; + let Ok(body)=body else { + handle_error(body.err().unwrap(), res); + return; + }; + let headers = req.headers(); + + // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime + let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); + let function = function.clone(); + let server_context = server_context.clone(); + spawn_blocking({ + move || { + tokio::runtime::Runtime::new() + .expect("couldn't spawn runtime") + .block_on(async move { + let resp = function(server_context, &body).await; + + resp_tx.send(resp).unwrap(); + }) + } + }); + let result = resp_rx.await.unwrap(); + + match result { + Ok(serialized) => { + // if this is Accept: application/json then send a serialized JSON response + let accept_header = headers.get("Accept").and_then(|value| value.to_str().ok()); + if accept_header == Some("application/json") + || accept_header + == Some( + "application/\ + x-www-form-urlencoded", + ) + || accept_header == Some("application/cbor") + { + res.set_status_code(StatusCode::OK); + } + + match serialized { + Payload::Binary(data) => { + res.headers_mut() + .insert("Content-Type", HeaderValue::from_static("application/cbor")); + res.write_body(data).unwrap(); + } + Payload::Url(data) => { + res.headers_mut().insert( + "Content-Type", + HeaderValue::from_static( + "application/\ + x-www-form-urlencoded", + ), + ); + res.render(data); + } + Payload::Json(data) => { + res.headers_mut() + .insert("Content-Type", HeaderValue::from_static("application/json")); + res.render(data); + } + } + } + Err(err) => handle_error(err, res), + } + } +} + +fn handle_error(error: impl Error + Send + Sync, res: &mut Response) { + let mut resp_err = Response::new(); + resp_err.set_status_code(StatusCode::INTERNAL_SERVER_ERROR); + resp_err.render(format!("Internal Server Error: {}", error)); + *res = resp_err; } From 0b80d32d187b6fe75352029a62366045e33b35c1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 30 Mar 2023 20:50:58 -0500 Subject: [PATCH 321/516] fix axum adapter --- packages/server/src/adapters/axum_adapter.rs | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index b7cc85e1c..9d99a697d 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -18,7 +18,10 @@ use crate::{ pub trait DioxusRouterExt { fn register_server_fns(self, server_fn_route: &'static str) -> Self; - fn serve_dioxus_application(self, cfg: ServeConfig) -> Self; + fn serve_dioxus_application( + self, + cfg: ServeConfig

, + ) -> Self; } impl DioxusRouterExt for Router { @@ -37,18 +40,24 @@ impl DioxusRouterExt for Router { router } - fn serve_dioxus_application(self, cfg: ServeConfig) -> Self { + fn serve_dioxus_application( + self, + cfg: ServeConfig

, + ) -> Self { use tower_http::services::ServeDir; // Serve the dist folder and the index.html file let serve_dir = ServeDir::new("dist"); self.register_server_fns(cfg.server_fn_route.unwrap_or_default()) - .route("/", get(move || { - let rendered = dioxus_ssr_html(cfg); - async move { Full::from(rendered) } - })) - .fallback_service( serve_dir) + .route( + "/", + get(move || { + let rendered = dioxus_ssr_html(&cfg); + async move { Full::from(rendered) } + }), + ) + .fallback_service(serve_dir) } } From 976d4ab960e0dd5d0ac84c690add93da7508a147 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 31 Mar 2023 09:40:58 -0500 Subject: [PATCH 322/516] Add warp adapter --- Cargo.toml | 1 + packages/server/Cargo.toml | 3 +- .../examples/warp-hello-world/.gitignore | 2 + .../examples/warp-hello-world/Cargo.toml | 20 +++ .../examples/warp-hello-world/src/main.rs | 65 ++++++++ packages/server/src/adapters/mod.rs | 2 + packages/server/src/adapters/salvo_adapter.rs | 4 +- packages/server/src/adapters/warp_adapter.rs | 150 +++++++++++++++--- packages/server/src/lib.rs | 2 + 9 files changed, 225 insertions(+), 24 deletions(-) create mode 100644 packages/server/examples/warp-hello-world/.gitignore create mode 100644 packages/server/examples/warp-hello-world/Cargo.toml create mode 100644 packages/server/examples/warp-hello-world/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index bf2b4b8c4..f4ad80086 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "packages/server/server-macro", "packages/server/examples/axum-hello-world", "packages/server/examples/salvo-hello-world", + "packages/server/examples/warp-hello-world", "docs/guide", ] diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index fc5f33c7f..ee0a826e9 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -11,6 +11,7 @@ server_macro = { path = "server-macro" } # warp warp = { version = "0.3.3", optional = true } +http-body = { version = "0.4.5", optional = true } # axum axum = { version = "0.6.1", optional = true } @@ -31,7 +32,7 @@ tokio = { version = "1.27.0", features = ["full"], optional = true } [features] default = [] -warp = ["dep:warp"] +warp = ["dep:warp", "http-body"] axum = ["dep:axum", "tower-http", "hyper"] salvo = ["dep:salvo", "hyper"] ssr = ["server_fn/ssr", "tokio", "dioxus-ssr"] diff --git a/packages/server/examples/warp-hello-world/.gitignore b/packages/server/examples/warp-hello-world/.gitignore new file mode 100644 index 000000000..6047329c6 --- /dev/null +++ b/packages/server/examples/warp-hello-world/.gitignore @@ -0,0 +1,2 @@ +dist +target \ No newline at end of file diff --git a/packages/server/examples/warp-hello-world/Cargo.toml b/packages/server/examples/warp-hello-world/Cargo.toml new file mode 100644 index 000000000..122eddf36 --- /dev/null +++ b/packages/server/examples/warp-hello-world/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "warp-hello-world" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } +dioxus = { path = "../../../dioxus" } +dioxus-server = { path = "../../" } +tokio = { version = "1.27.0", features = ["full"], optional = true } +serde = "1.0.159" +tracing-subscriber = "0.3.16" +tracing = "0.1.37" +warp = { version = "0.3.3", optional = true } + +[features] +ssr = ["warp", "tokio", "dioxus-server/ssr", "dioxus-server/warp"] +web = ["dioxus-web"] diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs new file mode 100644 index 000000000..2bac390e2 --- /dev/null +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -0,0 +1,65 @@ +//! Run with: +//! +//! ```sh +//! dioxus build --features web +//! cargo run --features ssr +//! ``` + +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_server::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + PostServerData::register().unwrap(); + GetServerData::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let routes = serve_dioxus_application( + ServeConfig::new(app, ()).head(r#"Hello World!"#), + ); + warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; + }); + } +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + let text = use_state(cx, || "...".to_string()); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![text]; + async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + } + }, + "Run a server function" + } + "Server said: {text}" + }) +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/server/src/adapters/mod.rs b/packages/server/src/adapters/mod.rs index 41bb4a403..0c292bd8d 100644 --- a/packages/server/src/adapters/mod.rs +++ b/packages/server/src/adapters/mod.rs @@ -2,3 +2,5 @@ pub mod axum_adapter; #[cfg(feature = "salvo")] pub mod salvo_adapter; +#[cfg(feature = "warp")] +pub mod warp_adapter; diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 20edc9364..f381e0b4a 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -132,12 +132,12 @@ impl ServerFnHandler { x-www-form-urlencoded", ), ); - res.render(data); + res.write_body(data).unwrap(); } Payload::Json(data) => { res.headers_mut() .insert("Content-Type", HeaderValue::from_static("application/json")); - res.render(data); + res.write_body(data).unwrap(); } } } diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index e5c821ce3..2577de61f 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -1,28 +1,136 @@ -use crate::{LiveViewError, LiveViewSocket}; -use futures_util::{SinkExt, StreamExt}; -use warp::ws::{Message, WebSocket}; +use std::{error::Error, sync::Arc}; -/// Convert a warp websocket into a LiveViewSocket -/// -/// This is required to launch a LiveView app using the warp web framework -pub fn warp_socket(ws: WebSocket) -> impl LiveViewSocket { - ws.map(transform_rx) - .with(transform_tx) - .sink_map_err(|_| LiveViewError::SendingFailed) +use server_fn::{Payload, ServerFunctionRegistry}; +use tokio::task::spawn_blocking; +use warp::{ + filters::BoxedFilter, + http::{Response, StatusCode}, + hyper::{body::Bytes, HeaderMap}, + path, Filter, Reply, +}; + +use crate::{ + dioxus_ssr_html, + serve::ServeConfig, + server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, +}; + +pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { + let mut filter: Option> = None; + for server_fn_path in DioxusServerFnRegistry::paths_registered() { + let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); + let full_route = format!("{server_fn_route}/{server_fn_path}") + .trim_start_matches('/') + .to_string(); + let route = path(full_route) + .and(warp::post()) + .and(warp::header::headers_cloned()) + .and(warp::body::bytes()) + .and_then(move |headers: HeaderMap, body| { + let func = func.clone(); + async move { server_fn_handler(DioxusServerContext {}, func, headers, body).await } + }) + .boxed(); + if let Some(boxed_filter) = filter.take() { + filter = Some(boxed_filter.or(route).unify().boxed()); + } else { + filter = Some(route.boxed()); + } + } + filter.expect("No server functions found") } -fn transform_rx(message: Result) -> Result { - // destructure the message into the buffer we got from warp - let msg = message - .map_err(|_| LiveViewError::SendingFailed)? - .into_bytes(); +pub fn serve_dioxus_application( + cfg: ServeConfig

, +) -> BoxedFilter<(impl Reply,)> { + // Serve the dist folder and the index.html file + let serve_dir = warp::fs::dir("./dist"); - // transform it back into a string, saving us the allocation - let msg = String::from_utf8(msg).map_err(|_| LiveViewError::SendingFailed)?; - - Ok(msg) + register_server_fns(cfg.server_fn_route.unwrap_or_default()) + .or(warp::path::end() + .and(warp::get()) + .map(move || warp::reply::html(dioxus_ssr_html(&cfg)))) + .or(serve_dir) + .boxed() } -async fn transform_tx(message: String) -> Result { - Ok(Message::text(message)) +#[derive(Debug)] +struct FailedToReadBody(String); + +impl warp::reject::Reject for FailedToReadBody {} + +#[derive(Debug)] +struct RecieveFailed(String); + +impl warp::reject::Reject for RecieveFailed {} + +async fn server_fn_handler( + server_context: DioxusServerContext, + function: Arc, + headers: HeaderMap, + body: Bytes, +) -> Result, warp::Rejection> { + // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime + let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); + spawn_blocking({ + move || { + tokio::runtime::Runtime::new() + .expect("couldn't spawn runtime") + .block_on(async { + let resp = match function(server_context, &body).await { + Ok(serialized) => { + // if this is Accept: application/json then send a serialized JSON response + let accept_header = + headers.get("Accept").and_then(|value| value.to_str().ok()); + let mut res = Response::builder(); + if accept_header == Some("application/json") + || accept_header + == Some( + "application/\ + x-www-form-urlencoded", + ) + || accept_header == Some("application/cbor") + { + res = res.status(StatusCode::OK); + } + + let resp = match serialized { + Payload::Binary(data) => res + .header("Content-Type", "application/cbor") + .body(Bytes::from(data)), + Payload::Url(data) => res + .header( + "Content-Type", + "application/\ + x-www-form-urlencoded", + ) + .body(Bytes::from(data)), + Payload::Json(data) => res + .header("Content-Type", "application/json") + .body(Bytes::from(data)), + }; + + Box::new(resp.unwrap()) + } + Err(e) => report_err(e), + }; + + if resp_tx.send(resp).is_err() { + eprintln!("Error sending response"); + } + }) + } + }); + resp_rx.await.map_err(|err| { + warp::reject::custom(RecieveFailed(format!("Failed to recieve response {err}"))) + }) +} + +fn report_err(e: E) -> Box { + Box::new( + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(format!("Error: {}", e)) + .unwrap(), + ) as Box } diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index e7559363a..1cdd2f786 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -11,6 +11,8 @@ pub mod prelude { pub use crate::adapters::axum_adapter::*; #[cfg(feature = "salvo")] pub use crate::adapters::salvo_adapter::*; + #[cfg(feature = "warp")] + pub use crate::adapters::warp_adapter::*; #[cfg(feature = "ssr")] pub use crate::serve::ServeConfig; pub use crate::server_fn::{DioxusServerContext, ServerFn}; From bfcb0f6eab33ed8f9ec2d9a9f0b36de51f257f70 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 31 Mar 2023 15:33:44 -0500 Subject: [PATCH 323/516] add an example illistrating intigration with the router --- Cargo.toml | 1 + packages/router/src/components/router.rs | 1 + packages/server/Cargo.toml | 2 + .../examples/axum-hello-world/Cargo.toml | 5 +- .../examples/axum-hello-world/src/main.rs | 2 + .../server/examples/axum-router/.gitignore | 2 + .../server/examples/axum-router/Cargo.toml | 21 +++ .../server/examples/axum-router/src/main.rs | 141 ++++++++++++++++++ .../examples/salvo-hello-world/Cargo.toml | 2 - .../examples/warp-hello-world/Cargo.toml | 2 - packages/server/src/adapters/axum_adapter.rs | 17 ++- packages/server/src/lib.rs | 53 +------ packages/server/src/render.rs | 92 ++++++++++++ packages/server/src/serve.rs | 8 - packages/ssr/src/renderer.rs | 6 +- 15 files changed, 283 insertions(+), 72 deletions(-) create mode 100644 packages/server/examples/axum-router/.gitignore create mode 100644 packages/server/examples/axum-router/Cargo.toml create mode 100644 packages/server/examples/axum-router/src/main.rs create mode 100644 packages/server/src/render.rs diff --git a/Cargo.toml b/Cargo.toml index f4ad80086..e2e427043 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "packages/server/examples/axum-hello-world", "packages/server/examples/salvo-hello-world", "packages/server/examples/warp-hello-world", + "packages/server/examples/axum-router", "docs/guide", ] diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index 54131a13a..b027386c5 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -29,6 +29,7 @@ pub struct RouterProps<'a> { pub active_class: Option<&'a str>, /// Set the initial url. + #[props(!optional, into)] pub initial_url: Option, } diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index ee0a826e9..fa0e0592a 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -17,6 +17,7 @@ http-body = { version = "0.4.5", optional = true } axum = { version = "0.6.1", optional = true } tower-http = { version = "0.4.0", optional = true, features = ["fs"] } hyper = { version = "0.14.25", optional = true } +axum-macros = "0.3.7" # salvo salvo = { version = "0.37.7", optional = true, features = ["serve-static"] } @@ -29,6 +30,7 @@ log = "0.4.17" once_cell = "1.17.1" thiserror = "1.0.40" tokio = { version = "1.27.0", features = ["full"], optional = true } +object-pool = "0.5.4" [features] default = [] diff --git a/packages/server/examples/axum-hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml index 54aaf17d3..68da7a0be 100644 --- a/packages/server/examples/axum-hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -8,13 +8,12 @@ edition = "2021" [dependencies] dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } dioxus = { path = "../../../dioxus" } +dioxus-router = { path = "../../../router" } dioxus-server = { path = "../../" } axum = { version = "0.6.12", optional = true } tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" -tracing-subscriber = "0.3.16" -tracing = "0.1.37" [features] ssr = ["axum", "tokio", "dioxus-server/ssr", "dioxus-server/axum"] -web = ["dioxus-web"] +web = ["dioxus-web", "dioxus-router/web"] diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index 4d4ab5e78..b4bc76390 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -25,7 +25,9 @@ fn main() { axum::Router::new() .serve_dioxus_application( ServeConfig::new(app, ()).head(r#"Hello World!"#), + None, ) + .with_state(SSRState::default()) .into_make_service(), ) .await diff --git a/packages/server/examples/axum-router/.gitignore b/packages/server/examples/axum-router/.gitignore new file mode 100644 index 000000000..6047329c6 --- /dev/null +++ b/packages/server/examples/axum-router/.gitignore @@ -0,0 +1,2 @@ +dist +target \ No newline at end of file diff --git a/packages/server/examples/axum-router/Cargo.toml b/packages/server/examples/axum-router/Cargo.toml new file mode 100644 index 000000000..d1d8ff81d --- /dev/null +++ b/packages/server/examples/axum-router/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "axum-router" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } +dioxus = { path = "../../../dioxus" } +dioxus-router = { path = "../../../router" } +dioxus-server = { path = "../../" } +axum = { version = "0.6.12", optional = true } +tokio = { version = "1.27.0", features = ["full"], optional = true } +serde = "1.0.159" +tower-http = { version = "0.4.0", features = ["fs"], optional = true } +http = { version = "0.2.9", optional = true } + +[features] +ssr = ["axum", "tokio", "dioxus-server/ssr", "dioxus-server/axum", "tower-http", "http"] +web = ["dioxus-web", "dioxus-router/web"] diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs new file mode 100644 index 000000000..8745aa7f1 --- /dev/null +++ b/packages/server/examples/axum-router/src/main.rs @@ -0,0 +1,141 @@ +//! Run with: +//! +//! ```sh +//! dioxus build --features web +//! cargo run --features ssr +//! ``` + +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_router::*; +use dioxus_server::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_with_props( + App, + AppProps { route: None }, + dioxus_web::Config::new().hydrate(true), + ); + #[cfg(feature = "ssr")] + { + use axum::extract::State; + PostServerData::register().unwrap(); + GetServerData::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + use tower_http::services::ServeDir; + + // Serve the dist/assets folder with the javascript and WASM files created by the CLI + let serve_dir = ServeDir::new("dist/assets"); + + axum::Server::bind(&addr) + .serve( + axum::Router::new() + // Register server functions + .register_server_fns("") + // Serve the static assets folder + .nest_service("/assets", serve_dir) + // If the path is unknown, render the application + .fallback( + move |uri: http::uri::Uri, State(ssr_state): State| { + let rendered = ssr_state.render( + &ServeConfig::new( + App, + AppProps { + route: Some(format!("http://{addr}{uri}")), + }, + ) + .head(r#"Hello World!"#), + ); + async move { axum::body::Full::from(rendered) } + }, + ) + .with_state(SSRState::default()) + .into_make_service(), + ) + .await + .unwrap(); + }); + } +} + +#[derive(Clone, Debug, Props, PartialEq)] +struct AppProps { + route: Option, +} + +fn App(cx: Scope) -> Element { + cx.render(rsx! { + Router { + initial_url: cx.props.route.clone(), + + Route { to: "/blog", + Link { + to: "/", + "Go to counter" + } + table { + tbody { + for _ in 0..100 { + tr { + for _ in 0..100 { + td { "hello world??" } + } + } + } + } + } + }, + // Fallback + Route { to: "", + Counter {} + }, + } + }) +} + +fn Counter(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + let text = use_state(cx, || "...".to_string()); + + cx.render(rsx! { + Link { + to: "/blog", + "Go to blog" + } + div{ + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![text]; + async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + } + }, + "Run a server function" + } + "Server said: {text}" + } + }) +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/server/examples/salvo-hello-world/Cargo.toml b/packages/server/examples/salvo-hello-world/Cargo.toml index 1ebe56dfc..9c81ee2a2 100644 --- a/packages/server/examples/salvo-hello-world/Cargo.toml +++ b/packages/server/examples/salvo-hello-world/Cargo.toml @@ -11,8 +11,6 @@ dioxus = { path = "../../../dioxus" } dioxus-server = { path = "../../" } tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" -tracing-subscriber = "0.3.16" -tracing = "0.1.37" salvo = { version = "0.37.9", optional = true } [features] diff --git a/packages/server/examples/warp-hello-world/Cargo.toml b/packages/server/examples/warp-hello-world/Cargo.toml index 122eddf36..7bb9aa23f 100644 --- a/packages/server/examples/warp-hello-world/Cargo.toml +++ b/packages/server/examples/warp-hello-world/Cargo.toml @@ -11,8 +11,6 @@ dioxus = { path = "../../../dioxus" } dioxus-server = { path = "../../" } tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" -tracing-subscriber = "0.3.16" -tracing = "0.1.37" warp = { version = "0.3.3", optional = true } [features] diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 9d99a697d..5eaff9bfc 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -2,6 +2,7 @@ use std::{error::Error, sync::Arc}; use axum::{ body::{self, Body, BoxBody, Full}, + extract::{FromRef, State}, http::{HeaderMap, Request, Response, StatusCode}, response::IntoResponse, routing::{get, post}, @@ -11,7 +12,7 @@ use server_fn::{Payload, ServerFunctionRegistry}; use tokio::task::spawn_blocking; use crate::{ - dioxus_ssr_html, + render::SSRState, serve::ServeConfig, server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, }; @@ -21,10 +22,15 @@ pub trait DioxusRouterExt { fn serve_dioxus_application( self, cfg: ServeConfig

, + server_fn_route: Option<&'static str>, ) -> Self; } -impl DioxusRouterExt for Router { +impl DioxusRouterExt for Router +where + SSRState: FromRef, + S: Send + Sync + Clone + 'static, +{ fn register_server_fns(self, server_fn_route: &'static str) -> Self { let mut router = self; for server_fn_path in DioxusServerFnRegistry::paths_registered() { @@ -43,17 +49,18 @@ impl DioxusRouterExt for Router { fn serve_dioxus_application( self, cfg: ServeConfig

, + server_fn_route: Option<&'static str>, ) -> Self { use tower_http::services::ServeDir; // Serve the dist folder and the index.html file let serve_dir = ServeDir::new("dist"); - self.register_server_fns(cfg.server_fn_route.unwrap_or_default()) + self.register_server_fns(server_fn_route.unwrap_or_default()) .route( "/", - get(move || { - let rendered = dioxus_ssr_html(&cfg); + get(move |State(ssr_state): State| { + let rendered = ssr_state.render(&cfg); async move { Full::from(rendered) } }), ) diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 1cdd2f786..9141c7199 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -3,6 +3,8 @@ use dioxus_core::prelude::*; mod adapters; #[cfg(feature = "ssr")] +pub mod render; +#[cfg(feature = "ssr")] mod serve; mod server_fn; @@ -14,57 +16,10 @@ pub mod prelude { #[cfg(feature = "warp")] pub use crate::adapters::warp_adapter::*; #[cfg(feature = "ssr")] + pub use crate::render::*; + #[cfg(feature = "ssr")] pub use crate::serve::ServeConfig; pub use crate::server_fn::{DioxusServerContext, ServerFn}; pub use server_fn::{self, ServerFn as _, ServerFnError}; pub use server_macro::*; } - -#[cfg(feature = "ssr")] -fn dioxus_ssr_html(cfg: &serve::ServeConfig

) -> String { - use prelude::ServeConfig; - - let ServeConfig { - app, - application_name, - base_path, - head, - props, - .. - } = cfg; - - let application_name = application_name.unwrap_or("dioxus"); - - let mut vdom = VirtualDom::new_with_props(*app, props.clone()); - let _ = vdom.rebuild(); - let renderered = dioxus_ssr::pre_render(&vdom); - let base_path = base_path.unwrap_or("."); - let head = head.unwrap_or( - r#"Dioxus Application - - - "#, - ); - format!( - r#" - - - - {head} - - -

- {renderered} -
- - -"# - ) -} diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs new file mode 100644 index 000000000..5c15bd097 --- /dev/null +++ b/packages/server/src/render.rs @@ -0,0 +1,92 @@ +use std::fmt::Write; +use std::sync::Arc; + +use dioxus_core::VirtualDom; +use dioxus_ssr::Renderer; + +use crate::prelude::ServeConfig; + +fn dioxus_ssr_html(cfg: &ServeConfig

, renderer: &mut Renderer) -> String { + let ServeConfig { + app, + application_name, + base_path, + head, + props, + .. + } = cfg; + + let application_name = application_name.unwrap_or("dioxus"); + let mut vdom = VirtualDom::new_with_props(*app, props.clone()); + let _ = vdom.rebuild(); + let base_path = base_path.unwrap_or("."); + let head = head.unwrap_or( + r#"Dioxus Application + + + "#, + ); + + let mut html = String::new(); + + if let Err(err) = write!( + &mut html, + r#" + + + {head} + + +

"# + ) { + eprintln!("Failed to write to html: {}", err); + } + + let _ = renderer.render_to(&mut html, &vdom); + + if let Err(err) = write!( + &mut html, + r#"
+ + + "# + ) { + eprintln!("Failed to write to html: {}", err); + } + + html +} + +#[derive(Clone)] +pub struct SSRState { + // We keep a cache of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move + renderers: Arc>, +} + +impl Default for SSRState { + fn default() -> Self { + Self { + renderers: Arc::new(object_pool::Pool::new(10, pre_renderer)), + } + } +} + +impl SSRState { + pub fn render(&self, cfg: &ServeConfig

) -> String { + let mut renderer = self.renderers.pull(pre_renderer); + dioxus_ssr_html(cfg, &mut renderer) + } +} + +fn pre_renderer() -> Renderer { + let mut renderer = Renderer::default(); + renderer.pre_render = true; + renderer +} diff --git a/packages/server/src/serve.rs b/packages/server/src/serve.rs index 9cca17a6b..65f0871ae 100644 --- a/packages/server/src/serve.rs +++ b/packages/server/src/serve.rs @@ -5,7 +5,6 @@ pub struct ServeConfig { pub(crate) app: Component

, pub(crate) props: P, pub(crate) application_name: Option<&'static str>, - pub(crate) server_fn_route: Option<&'static str>, pub(crate) base_path: Option<&'static str>, pub(crate) head: Option<&'static str>, } @@ -17,7 +16,6 @@ impl ServeConfig

{ app, props, application_name: None, - server_fn_route: None, base_path: None, head: None, } @@ -29,12 +27,6 @@ impl ServeConfig

{ self } - /// Set the base route all server functions will be served under - pub fn server_fn_route(mut self, server_fn_route: &'static str) -> Self { - self.server_fn_route = Some(server_fn_route); - self - } - /// Set the path the WASM application will be served under pub fn base_path(mut self, base_path: &'static str) -> Self { self.base_path = Some(base_path); diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index 5b6fa8fb2..d9c312ac7 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -3,7 +3,7 @@ use crate::cache::StringCache; use dioxus_core::{prelude::*, AttributeValue, DynamicNode, RenderReturn}; use std::collections::HashMap; use std::fmt::Write; -use std::rc::Rc; +use std::sync::Arc; /// A virtualdom renderer that caches the templates it has seen for faster rendering #[derive(Default)] @@ -25,7 +25,7 @@ pub struct Renderer { pub skip_components: bool, /// A cache of templates that have been rendered - template_cache: HashMap<&'static str, Rc>, + template_cache: HashMap<&'static str, Arc>, } impl Renderer { @@ -67,7 +67,7 @@ impl Renderer { let entry = self .template_cache .entry(template.template.get().name) - .or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap())) + .or_insert_with(|| Arc::new(StringCache::from_template(template).unwrap())) .clone(); for segment in entry.segments.iter() { From 5ffdb4dbede3e8806566a0debd69d0442c8521a9 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 31 Mar 2023 16:16:47 -0500 Subject: [PATCH 324/516] prefech wasm + JS by default --- .../server/examples/axum-router/src/main.rs | 15 ++++----- packages/server/src/render.rs | 31 +++++++++++++------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs index 8745aa7f1..0ae68d50e 100644 --- a/packages/server/examples/axum-router/src/main.rs +++ b/packages/server/examples/axum-router/src/main.rs @@ -41,15 +41,12 @@ fn main() { // If the path is unknown, render the application .fallback( move |uri: http::uri::Uri, State(ssr_state): State| { - let rendered = ssr_state.render( - &ServeConfig::new( - App, - AppProps { - route: Some(format!("http://{addr}{uri}")), - }, - ) - .head(r#"Hello World!"#), - ); + let rendered = ssr_state.render(&ServeConfig::new( + App, + AppProps { + route: Some(format!("http://{addr}{uri}")), + }, + )); async move { axum::body::Full::from(rendered) } }, ) diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 5c15bd097..872a6cffc 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -20,25 +20,36 @@ fn dioxus_ssr_html(cfg: &ServeConfig

, renderer: &mut Rend let mut vdom = VirtualDom::new_with_props(*app, props.clone()); let _ = vdom.rebuild(); let base_path = base_path.unwrap_or("."); - let head = head.unwrap_or( - r#"Dioxus Application - - - "#, - ); let mut html = String::new(); - if let Err(err) = write!( - &mut html, - r#" + let result = match head { + Some(head) => { + write!( + &mut html, + r#" {head}

"# - ) { + ) + } + None => { + write!( + &mut html, + r#"Dioxus Application + + + + + "# + ) + } + }; + + if let Err(err) = result { eprintln!("Failed to write to html: {}", err); } From 71ddd50963b9aae55386ba64936d217a9bf6b5b7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 1 Apr 2023 17:00:09 -0500 Subject: [PATCH 325/516] provide a nicer builder API --- .../examples/axum-hello-world/src/main.rs | 6 +-- .../server/examples/axum-router/src/main.rs | 17 ++++--- .../examples/salvo-hello-world/src/main.rs | 5 +- .../examples/warp-hello-world/src/main.rs | 4 +- packages/server/src/render.rs | 28 ++--------- packages/server/src/serve.rs | 47 ++++++++++++++++++- 6 files changed, 64 insertions(+), 43 deletions(-) diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index b4bc76390..fc07c6fbb 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -23,11 +23,7 @@ fn main() { axum::Server::bind(&addr) .serve( axum::Router::new() - .serve_dioxus_application( - ServeConfig::new(app, ()).head(r#"Hello World!"#), - None, - ) - .with_state(SSRState::default()) + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) .into_make_service(), ) .await diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs index 0ae68d50e..53193859c 100644 --- a/packages/server/examples/axum-router/src/main.rs +++ b/packages/server/examples/axum-router/src/main.rs @@ -41,12 +41,15 @@ fn main() { // If the path is unknown, render the application .fallback( move |uri: http::uri::Uri, State(ssr_state): State| { - let rendered = ssr_state.render(&ServeConfig::new( - App, - AppProps { - route: Some(format!("http://{addr}{uri}")), - }, - )); + let rendered = ssr_state.render( + &ServeConfigBuilder::new( + App, + AppProps { + route: Some(format!("http://{addr}{uri}")), + }, + ) + .build(), + ); async move { axum::body::Full::from(rendered) } }, ) @@ -79,7 +82,7 @@ fn App(cx: Scope) -> Element { for _ in 0..100 { tr { for _ in 0..100 { - td { "hello world??" } + td { "hello world!" } } } } diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs index cd8b80c51..f13668c5e 100644 --- a/packages/server/examples/salvo-hello-world/src/main.rs +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -20,9 +20,8 @@ fn main() { tokio::runtime::Runtime::new() .unwrap() .block_on(async move { - let router = Router::new().serve_dioxus_application( - ServeConfig::new(app, ()).head(r#"Hello World!"#), - ); + let router = + Router::new().serve_dioxus_application("", ServeConfigBuilder::new(app, ())); Server::new(TcpListener::bind("127.0.0.1:8080")) .serve(router) .await; diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs index 2bac390e2..87f91782a 100644 --- a/packages/server/examples/warp-hello-world/src/main.rs +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -19,9 +19,7 @@ fn main() { tokio::runtime::Runtime::new() .unwrap() .block_on(async move { - let routes = serve_dioxus_application( - ServeConfig::new(app, ()).head(r#"Hello World!"#), - ); + let routes = serve_dioxus_application("", ServeConfigBuilder::new(app, ())); warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; }); } diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 872a6cffc..cb752eaed 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -16,38 +16,20 @@ fn dioxus_ssr_html(cfg: &ServeConfig

, renderer: &mut Rend .. } = cfg; - let application_name = application_name.unwrap_or("dioxus"); let mut vdom = VirtualDom::new_with_props(*app, props.clone()); let _ = vdom.rebuild(); - let base_path = base_path.unwrap_or("."); let mut html = String::new(); - let result = match head { - Some(head) => { - write!( - &mut html, - r#" + let result = write!( + &mut html, + r#" {head} - - +

"# - ) - } - None => { - write!( - &mut html, - r#"Dioxus Application - - - - - "# - ) - } - }; + ); if let Err(err) = result { eprintln!("Failed to write to html: {}", err); diff --git a/packages/server/src/serve.rs b/packages/server/src/serve.rs index 65f0871ae..ed059f47f 100644 --- a/packages/server/src/serve.rs +++ b/packages/server/src/serve.rs @@ -1,15 +1,16 @@ use dioxus_core::Component; #[derive(Clone)] -pub struct ServeConfig { +pub struct ServeConfigBuilder { pub(crate) app: Component

, pub(crate) props: P, pub(crate) application_name: Option<&'static str>, pub(crate) base_path: Option<&'static str>, pub(crate) head: Option<&'static str>, + pub(crate) assets_path: Option<&'static str>, } -impl ServeConfig

{ +impl ServeConfigBuilder

{ /// Create a new ServeConfig pub fn new(app: Component

, props: P) -> Self { Self { @@ -18,6 +19,7 @@ impl ServeConfig

{ application_name: None, base_path: None, head: None, + assets_path: None, } } @@ -38,4 +40,45 @@ impl ServeConfig

{ self.head = Some(head); self } + + /// Set the path of the assets folder generated by the Dioxus CLI. (defaults to dist/assets) + pub fn assets_path(mut self, assets_path: &'static str) -> Self { + self.assets_path = Some(assets_path); + self + } + + /// Build the ServeConfig + pub fn build(self) -> ServeConfig

{ + let base_path = self.base_path.unwrap_or("."); + let application_name = self.application_name.unwrap_or("dioxus"); + ServeConfig { + app: self.app, + props: self.props, + application_name, + base_path, + head: self.head.map(String::from).unwrap_or(format!(r#"Dioxus Application + + + + + "#)), + assets_path: self.assets_path.unwrap_or("dist/assets"), + } + } +} + +#[derive(Clone)] +pub struct ServeConfig { + pub(crate) app: Component

, + pub(crate) props: P, + pub(crate) application_name: &'static str, + pub(crate) base_path: &'static str, + pub(crate) head: String, + pub(crate) assets_path: &'static str, +} + +impl From> for ServeConfig

{ + fn from(builder: ServeConfigBuilder

) -> Self { + builder.build() + } } From c6992c7032474eaeda4bb8371cf64d22dada2981 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 1 Apr 2023 17:00:12 -0500 Subject: [PATCH 326/516] make server function API more flexible --- packages/server/Cargo.toml | 3 +- packages/server/server-macro/Cargo.toml | 3 +- packages/server/server-macro/src/lib.rs | 14 ++-- packages/server/src/adapters/axum_adapter.rs | 81 +++++++++++++------ packages/server/src/adapters/salvo_adapter.rs | 69 ++++++++++++---- packages/server/src/adapters/warp_adapter.rs | 69 +++++++++++----- packages/server/src/lib.rs | 8 +- packages/server/src/server_context.rs | 35 ++++++++ packages/server/src/server_fn.rs | 3 +- 9 files changed, 209 insertions(+), 76 deletions(-) create mode 100644 packages/server/src/server_context.rs diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index fa0e0592a..2485a2529 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -server_fn = { version = "0.2.4", features = ["stable"] } +server_fn = { path = "D:/Users/Desktop/github/leptos/server_fn", features = ["stable"] } server_macro = { path = "server-macro" } # warp @@ -31,6 +31,7 @@ once_cell = "1.17.1" thiserror = "1.0.40" tokio = { version = "1.27.0", features = ["full"], optional = true } object-pool = "0.5.4" +anymap = "0.12.1" [features] default = [] diff --git a/packages/server/server-macro/Cargo.toml b/packages/server/server-macro/Cargo.toml index cb285f517..e5fb6523e 100644 --- a/packages/server/server-macro/Cargo.toml +++ b/packages/server/server-macro/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" [dependencies] quote = "1.0.26" -server_fn_macro = { version = "0.2.4", features = ["stable"] } +# server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "1e037ecb60965c7c55fd781fdc8de7863ffd102b", features = ["stable"] } +server_fn_macro = { path = "D:/Users/Desktop/github/leptos/server_fn_macro", features = ["stable"] } syn = { version = "1", features = ["full"] } [lib] diff --git a/packages/server/server-macro/src/lib.rs b/packages/server/server-macro/src/lib.rs index f2a2e1665..f6b4f92a2 100644 --- a/packages/server/server-macro/src/lib.rs +++ b/packages/server/server-macro/src/lib.rs @@ -2,7 +2,7 @@ use proc_macro::TokenStream; use quote::ToTokens; use server_fn_macro::*; -/// Declares that a function is a [server function](leptos_server). This means that +/// Declares that a function is a [server function](dioxus_server). This means that /// its body will only run on the server, i.e., when the `ssr` feature is enabled. /// /// If you call a server function from the client (i.e., when the `csr` or `hydrate` features @@ -19,18 +19,18 @@ use server_fn_macro::*; /// work without WebAssembly, the encoding must be `"Url"`. /// /// The server function itself can take any number of arguments, each of which should be serializable -/// and deserializable with `serde`. Optionally, its first argument can be a Leptos [Scope](leptos_reactive::Scope), +/// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](dioxus_server::prelude::DioxusServerContext), /// which will be injected *on the server side.* This can be used to inject the raw HTTP request or other /// server-side context into the server function. /// /// ```ignore -/// # use leptos::*; use serde::{Serialize, Deserialize}; +/// # use dioxus_server::prelude::*; use serde::{Serialize, Deserialize}; /// # #[derive(Serialize, Deserialize)] /// # pub struct Post { } /// #[server(ReadPosts, "/api")] /// pub async fn read_posts(how_many: u8, query: String) -> Result, ServerFnError> { /// // do some work on the server to access the database -/// todo!() +/// todo!() /// } /// ``` /// @@ -42,7 +42,7 @@ use server_fn_macro::*; /// - **Server functions must return `Result`.** Even if the work being done /// inside the function body can’t fail, the processes of serialization/deserialization and the /// network call are fallible. -/// - **Return types must be [Serializable](leptos_reactive::Serializable).** +/// - **Return types must implement [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html).** /// This should be fairly obvious: we have to serialize arguments to send them to the server, and we /// need to deserialize the result to return it to the client. /// - **Arguments must be implement [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) @@ -50,8 +50,8 @@ use server_fn_macro::*; /// They are serialized as an `application/x-www-form-urlencoded` /// form data using [`serde_urlencoded`](https://docs.rs/serde_urlencoded/latest/serde_urlencoded/) or as `application/cbor` /// using [`cbor`](https://docs.rs/cbor/latest/cbor/). -/// - **The [Scope](leptos_reactive::Scope) comes from the server.** Optionally, the first argument of a server function -/// can be a Leptos [Scope](leptos_reactive::Scope). This scope can be used to inject dependencies like the HTTP request +/// - **The [DioxusServerContext](dioxus_server::prelude::DioxusServerContext) comes from the server.** Optionally, the first argument of a server function +/// can be a [DioxusServerContext](dioxus_server::prelude::DioxusServerContext). This scope can be used to inject dependencies like the HTTP request /// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client. #[proc_macro_attribute] pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 5eaff9bfc..8fb98b6ec 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -2,7 +2,8 @@ use std::{error::Error, sync::Arc}; use axum::{ body::{self, Body, BoxBody, Full}, - extract::{FromRef, State}, + extract::State, + handler::Handler, http::{HeaderMap, Request, Response, StatusCode}, response::IntoResponse, routing::{get, post}, @@ -14,61 +15,89 @@ use tokio::task::spawn_blocking; use crate::{ render::SSRState, serve::ServeConfig, - server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, + server_context::DioxusServerContext, + server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, }; -pub trait DioxusRouterExt { +pub trait DioxusRouterExt { + fn register_server_fns_with_handler( + self, + server_fn_route: &'static str, + handler: impl Fn(Arc) -> H, + ) -> Self + where + H: Handler, + T: 'static, + S: Clone + Send + Sync + 'static; fn register_server_fns(self, server_fn_route: &'static str) -> Self; + fn serve_dioxus_application( self, - cfg: ServeConfig

, - server_fn_route: Option<&'static str>, + server_fn_route: &'static str, + cfg: impl Into>, ) -> Self; } -impl DioxusRouterExt for Router +impl DioxusRouterExt for Router where - SSRState: FromRef, S: Send + Sync + Clone + 'static, { - fn register_server_fns(self, server_fn_route: &'static str) -> Self { + fn register_server_fns_with_handler( + self, + server_fn_route: &'static str, + mut handler: impl FnMut(Arc) -> H, + ) -> Self + where + H: Handler, + T: 'static, + S: Clone + Send + Sync + 'static, + { let mut router = self; for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - router = router.route( - &full_route, - post(move |headers: HeaderMap, body: Request| async move { - server_fn_handler(DioxusServerContext {}, func.clone(), headers, body).await - }), - ); + router = router.route(&full_route, post(handler(func))); } router } + fn register_server_fns(self, server_fn_route: &'static str) -> Self { + self.register_server_fns_with_handler(server_fn_route, |func| { + move |headers: HeaderMap, body: Request| async move { + server_fn_handler(DioxusServerContext::default(), func.clone(), headers, body).await + } + }) + } + fn serve_dioxus_application( self, - cfg: ServeConfig

, - server_fn_route: Option<&'static str>, + server_fn_route: &'static str, + cfg: impl Into>, ) -> Self { use tower_http::services::ServeDir; - // Serve the dist folder and the index.html file - let serve_dir = ServeDir::new("dist"); + let cfg = cfg.into(); - self.register_server_fns(server_fn_route.unwrap_or_default()) - .route( + // Serve the dist folder and the index.html file + let serve_dir = ServeDir::new(cfg.assets_path); + + self.register_server_fns(server_fn_route) + .nest_service("/assets", serve_dir) + .route_service( "/", - get(move |State(ssr_state): State| { - let rendered = ssr_state.render(&cfg); - async move { Full::from(rendered) } - }), + get(render_handler).with_state((cfg, SSRState::default())), ) - .fallback_service(serve_dir) } } -async fn server_fn_handler( +async fn render_handler( + State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, +) -> impl IntoResponse { + let rendered = ssr_state.render(&cfg); + Full::from(rendered) +} + +pub async fn server_fn_handler( server_context: DioxusServerContext, function: Arc, headers: HeaderMap, diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index f381e0b4a..0d8a27b22 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -9,43 +9,65 @@ use server_fn::{Payload, ServerFunctionRegistry}; use tokio::task::spawn_blocking; use crate::{ - dioxus_ssr_html, + prelude::DioxusServerContext, + prelude::SSRState, serve::ServeConfig, - server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, + server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, }; pub trait DioxusRouterExt { fn register_server_fns(self, server_fn_route: &'static str) -> Self; + fn register_server_fns_with_handler( + self, + server_fn_route: &'static str, + handler: impl Fn(Arc) -> H, + ) -> Self + where + H: Handler + 'static; fn serve_dioxus_application( self, - cfg: ServeConfig

, + server_fn_path: &'static str, + cfg: impl Into>, ) -> Self; } impl DioxusRouterExt for Router { - fn register_server_fns(self, server_fn_route: &'static str) -> Self { + fn register_server_fns_with_handler( + self, + server_fn_route: &'static str, + mut handler: impl FnMut(Arc) -> H, + ) -> Self + where + H: Handler + 'static, + { let mut router = self; for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - router = router.push(Router::with_path(&full_route).post(ServerFnHandler { - server_context: DioxusServerContext {}, - function: func, - })); + router = router.push(Router::with_path(&full_route).post(handler(func))); } router } + fn register_server_fns(self, server_fn_route: &'static str) -> Self { + self.register_server_fns_with_handler(|| ServerFnHandler { + server_context: DioxusServerContext::default(), + function: func, + }) + } + fn serve_dioxus_application( self, - cfg: ServeConfig

, + server_fn_route: &'static str, + cfg: impl Into>, ) -> Self { + let cfg = cfg.into(); // Serve the dist folder and the index.html file - let serve_dir = StaticDir::new(["dist"]); + let serve_dir = StaticDir::new([cfg.assets_path]); - self.register_server_fns(cfg.server_fn_route.unwrap_or_default()) + self.register_server_fns(server_fn_route) .push(Router::with_path("/").get(SSRHandler { cfg })) - .push(Router::with_path("<**path>").get(serve_dir)) + .push(Router::with_path("assets/<**path>").get(serve_dir)) } } @@ -58,19 +80,36 @@ impl Handler for SSRHandler

{ async fn handle( &self, _req: &mut Request, - _depot: &mut Depot, + depot: &mut Depot, res: &mut Response, _flow: &mut FlowCtrl, ) { - res.write_body(dioxus_ssr_html(&self.cfg)).unwrap(); + // Get the SSR renderer from the depot or create a new one if it doesn't exist + let renderer_pool = if let Some(renderer) = depot.obtain::() { + renderer.clone() + } else { + let renderer = SSRState::default(); + depot.inject(renderer.clone()); + renderer + }; + res.write_body(renderer_pool.render(&self.cfg)).unwrap(); } } -struct ServerFnHandler { +pub struct ServerFnHandler { server_context: DioxusServerContext, function: Arc, } +impl ServerFnHandler { + pub fn new(server_context: DioxusServerContext, function: Arc) -> Self { + Self { + server_context, + function, + } + } +} + #[handler] impl ServerFnHandler { async fn handle(&self, req: &mut Request, _depot: &mut Depot, res: &mut Response) { diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 2577de61f..00ebb38fa 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -10,50 +10,75 @@ use warp::{ }; use crate::{ - dioxus_ssr_html, + prelude::{DioxusServerContext, SSRState}, serve::ServeConfig, - server_fn::{DioxusServerContext, DioxusServerFnRegistry, ServerFnTraitObj}, + server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, }; -pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { - let mut filter: Option> = None; +pub fn register_server_fns_with_handler( + server_fn_route: &'static str, + mut handler: H, +) -> BoxedFilter<(R,)> +where + H: FnMut(String, Arc) -> F, + F: Filter + Send + Sync + 'static, + F::Extract: Send, + R: Reply + 'static, +{ + let mut filter: Option> = None; for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}") .trim_start_matches('/') .to_string(); - let route = path(full_route) - .and(warp::post()) - .and(warp::header::headers_cloned()) - .and(warp::body::bytes()) - .and_then(move |headers: HeaderMap, body| { - let func = func.clone(); - async move { server_fn_handler(DioxusServerContext {}, func, headers, body).await } - }) - .boxed(); + let route = handler(full_route, func.clone()).boxed(); if let Some(boxed_filter) = filter.take() { filter = Some(boxed_filter.or(route).unify().boxed()); } else { - filter = Some(route.boxed()); + filter = Some(route); } } filter.expect("No server functions found") } -pub fn serve_dioxus_application( - cfg: ServeConfig

, -) -> BoxedFilter<(impl Reply,)> { - // Serve the dist folder and the index.html file - let serve_dir = warp::fs::dir("./dist"); +pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { + register_server_fns_with_handler(server_fn_route, |full_route, func| { + path(full_route) + .and(warp::post()) + .and(warp::header::headers_cloned()) + .and(warp::body::bytes()) + .and_then(move |headers: HeaderMap, body| { + let func = func.clone(); + async move { + server_fn_handler(DioxusServerContext::default(), func, headers, body).await + } + }) + }) +} - register_server_fns(cfg.server_fn_route.unwrap_or_default()) +pub fn serve_dioxus_application( + server_fn_route: &'static str, + cfg: impl Into>, +) -> BoxedFilter<(impl Reply,)> { + let cfg = cfg.into(); + // Serve the dist folder and the index.html file + let serve_dir = warp::fs::dir(cfg.assets_path); + + register_server_fns(server_fn_route) .or(warp::path::end() .and(warp::get()) - .map(move || warp::reply::html(dioxus_ssr_html(&cfg)))) - .or(serve_dir) + .and(with_ssr_state()) + .map(move |renderer: SSRState| warp::reply::html(renderer.render(&cfg)))) + .or(warp::path("assets").and(serve_dir)) .boxed() } +fn with_ssr_state() -> impl Filter + Clone +{ + let renderer = SSRState::default(); + warp::any().map(move || renderer.clone()) +} + #[derive(Debug)] struct FailedToReadBody(String); diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 9141c7199..a9c8e4939 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -6,6 +6,7 @@ mod adapters; pub mod render; #[cfg(feature = "ssr")] mod serve; +mod server_context; mod server_fn; pub mod prelude { @@ -18,8 +19,11 @@ pub mod prelude { #[cfg(feature = "ssr")] pub use crate::render::*; #[cfg(feature = "ssr")] - pub use crate::serve::ServeConfig; - pub use crate::server_fn::{DioxusServerContext, ServerFn}; + pub use crate::serve::{ServeConfig, ServeConfigBuilder}; + pub use crate::server_context::DioxusServerContext; + pub use crate::server_fn::ServerFn; + #[cfg(feature = "ssr")] + pub use crate::server_fn::ServerFnTraitObj; pub use server_fn::{self, ServerFn as _, ServerFnError}; pub use server_macro::*; } diff --git a/packages/server/src/server_context.rs b/packages/server/src/server_context.rs new file mode 100644 index 000000000..427ee21f8 --- /dev/null +++ b/packages/server/src/server_context.rs @@ -0,0 +1,35 @@ +use std::sync::{Arc, PoisonError, RwLock, RwLockWriteGuard}; + +use anymap::{any::Any, Map}; + +type SendSyncAnyMap = Map; + +/// A shared context for server functions. This allows you to pass data between your server and the server functions like authentication session data. +#[derive(Clone)] +pub struct DioxusServerContext { + shared_context: Arc>, +} + +impl Default for DioxusServerContext { + fn default() -> Self { + Self { + shared_context: Arc::new(RwLock::new(SendSyncAnyMap::new())), + } + } +} + +impl DioxusServerContext { + pub fn get(&self) -> Option { + self.shared_context.read().ok()?.get::().cloned() + } + + pub fn insert( + &mut self, + value: T, + ) -> Result<(), PoisonError>> { + self.shared_context + .write() + .map(|mut map| map.insert(value)) + .map(|_| ()) + } +} diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs index 2f000adda..7170d61de 100644 --- a/packages/server/src/server_fn.rs +++ b/packages/server/src/server_fn.rs @@ -1,5 +1,4 @@ -#[derive(Clone)] -pub struct DioxusServerContext {} +use crate::server_context::DioxusServerContext; #[cfg(any(feature = "ssr", doc))] pub type ServerFnTraitObj = server_fn::ServerFnTraitObj; From 6a51f8998d8c012454122e4b7afbb84d2238d1f3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 1 Apr 2023 17:05:44 -0500 Subject: [PATCH 327/516] simplify server package features --- packages/server/Cargo.toml | 6 +++--- packages/server/examples/axum-hello-world/Cargo.toml | 3 ++- packages/server/examples/axum-hello-world/src/main.rs | 2 +- packages/server/examples/axum-router/Cargo.toml | 3 ++- packages/server/examples/axum-router/src/main.rs | 2 +- packages/server/examples/salvo-hello-world/Cargo.toml | 3 ++- packages/server/examples/salvo-hello-world/src/main.rs | 2 +- packages/server/examples/warp-hello-world/Cargo.toml | 3 ++- packages/server/examples/warp-hello-world/src/main.rs | 2 +- 9 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 2485a2529..5828875be 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -35,7 +35,7 @@ anymap = "0.12.1" [features] default = [] -warp = ["dep:warp", "http-body"] -axum = ["dep:axum", "tower-http", "hyper"] -salvo = ["dep:salvo", "hyper"] +warp = ["dep:warp", "http-body", "ssr"] +axum = ["dep:axum", "tower-http", "hyper", "ssr"] +salvo = ["dep:salvo", "hyper", "ssr"] ssr = ["server_fn/ssr", "tokio", "dioxus-ssr"] diff --git a/packages/server/examples/axum-hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml index 68da7a0be..ed466e6e1 100644 --- a/packages/server/examples/axum-hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -15,5 +15,6 @@ tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" [features] -ssr = ["axum", "tokio", "dioxus-server/ssr", "dioxus-server/axum"] +default = ["web"] +ssr = ["axum", "tokio", "dioxus-server/axum"] web = ["dioxus-web", "dioxus-router/web"] diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index fc07c6fbb..899247af5 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -2,7 +2,7 @@ //! //! ```sh //! dioxus build --features web -//! cargo run --features ssr +//! cargo run --features ssr --no-default-features //! ``` #![allow(non_snake_case)] diff --git a/packages/server/examples/axum-router/Cargo.toml b/packages/server/examples/axum-router/Cargo.toml index d1d8ff81d..297aa6726 100644 --- a/packages/server/examples/axum-router/Cargo.toml +++ b/packages/server/examples/axum-router/Cargo.toml @@ -17,5 +17,6 @@ tower-http = { version = "0.4.0", features = ["fs"], optional = true } http = { version = "0.2.9", optional = true } [features] -ssr = ["axum", "tokio", "dioxus-server/ssr", "dioxus-server/axum", "tower-http", "http"] +default = ["web"] +ssr = ["axum", "tokio", "dioxus-server/axum", "tower-http", "http"] web = ["dioxus-web", "dioxus-router/web"] diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs index 53193859c..2afa4a481 100644 --- a/packages/server/examples/axum-router/src/main.rs +++ b/packages/server/examples/axum-router/src/main.rs @@ -2,7 +2,7 @@ //! //! ```sh //! dioxus build --features web -//! cargo run --features ssr +//! cargo run --features ssr --no-default-features //! ``` #![allow(non_snake_case)] diff --git a/packages/server/examples/salvo-hello-world/Cargo.toml b/packages/server/examples/salvo-hello-world/Cargo.toml index 9c81ee2a2..4633eaafc 100644 --- a/packages/server/examples/salvo-hello-world/Cargo.toml +++ b/packages/server/examples/salvo-hello-world/Cargo.toml @@ -14,5 +14,6 @@ serde = "1.0.159" salvo = { version = "0.37.9", optional = true } [features] -ssr = ["salvo", "tokio", "dioxus-server/ssr", "dioxus-server/salvo"] +default = ["web"] +ssr = ["salvo", "tokio", "dioxus-server/salvo"] web = ["dioxus-web"] diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs index f13668c5e..d1e654fee 100644 --- a/packages/server/examples/salvo-hello-world/src/main.rs +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -2,7 +2,7 @@ //! //! ```sh //! dioxus build --features web -//! cargo run --features ssr +//! cargo run --features ssr --no-default-features //! ``` #![allow(non_snake_case)] diff --git a/packages/server/examples/warp-hello-world/Cargo.toml b/packages/server/examples/warp-hello-world/Cargo.toml index 7bb9aa23f..86ac2da5b 100644 --- a/packages/server/examples/warp-hello-world/Cargo.toml +++ b/packages/server/examples/warp-hello-world/Cargo.toml @@ -14,5 +14,6 @@ serde = "1.0.159" warp = { version = "0.3.3", optional = true } [features] -ssr = ["warp", "tokio", "dioxus-server/ssr", "dioxus-server/warp"] +default = ["web"] +ssr = ["warp", "tokio", "dioxus-server/warp"] web = ["dioxus-web"] diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs index 87f91782a..51a627d16 100644 --- a/packages/server/examples/warp-hello-world/src/main.rs +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -2,7 +2,7 @@ //! //! ```sh //! dioxus build --features web -//! cargo run --features ssr +//! cargo run --features ssr --no-default-features //! ``` #![allow(non_snake_case)] From f96425e4259ab2b0b881af0faf1f3ecbd3da1dd2 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 2 Apr 2023 15:07:51 -0500 Subject: [PATCH 328/516] collect templates for hot reloading --- packages/server/Cargo.toml | 8 ++- packages/server/src/render.rs | 114 ++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 47 deletions(-) diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 5828875be..086448ca1 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# server functions server_fn = { path = "D:/Users/Desktop/github/leptos/server_fn", features = ["stable"] } server_macro = { path = "server-macro" } @@ -23,6 +24,7 @@ axum-macros = "0.3.7" salvo = { version = "0.37.7", optional = true, features = ["serve-static"] } serde = "1.0.159" +# Dioxus + SSR dioxus-core = { path = "../core", version = "^0.3.0" } dioxus-ssr = { path = "../ssr", version = "^0.3.0", optional = true } @@ -33,8 +35,12 @@ tokio = { version = "1.27.0", features = ["full"], optional = true } object-pool = "0.5.4" anymap = "0.12.1" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +dioxus-hot-reload = { path = "../hot-reload" } + [features] -default = [] +default = ["hot-reload"] +hot-reload = [] warp = ["dep:warp", "http-body", "ssr"] axum = ["dep:axum", "tower-http", "hyper", "ssr"] salvo = ["dep:salvo", "hyper", "ssr"] diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index cb752eaed..bf8c12854 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -6,40 +6,82 @@ use dioxus_ssr::Renderer; use crate::prelude::ServeConfig; -fn dioxus_ssr_html(cfg: &ServeConfig

, renderer: &mut Renderer) -> String { - let ServeConfig { - app, - application_name, - base_path, - head, - props, - .. - } = cfg; +#[derive(Clone)] +pub struct SSRState { + // We keep a cache of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move + renderers: Arc>, + #[cfg(all(debug_assertions, feature = "hot-reload"))] + // The cache of all templates that have been modified since the last time we checked + templates: Arc>>>, +} - let mut vdom = VirtualDom::new_with_props(*app, props.clone()); - let _ = vdom.rebuild(); +impl Default for SSRState { + fn default() -> Self { + #[cfg(all(debug_assertions, feature = "hot-reload"))] + let templates = { + let templates = Arc::new(std::sync::RwLock::new(std::collections::HashSet::new())); + dioxus_hot_reload::connect({ + let templates = templates.clone(); + move |msg| match msg { + dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => { + if let Ok(mut templates) = templates.write() { + templates.insert(template); + } + } + dioxus_hot_reload::HotReloadMsg::Shutdown => { + std::process::exit(0); + } + } + }); + templates + }; - let mut html = String::new(); + Self { + renderers: Arc::new(object_pool::Pool::new(10, pre_renderer)), + #[cfg(all(debug_assertions, feature = "hot-reload"))] + templates, + } + } +} - let result = write!( - &mut html, - r#" +impl SSRState { + pub fn render(&self, cfg: &ServeConfig

) -> String { + let ServeConfig { + app, + application_name, + base_path, + head, + props, + .. + } = cfg; + + let mut vdom = VirtualDom::new_with_props(*app, props.clone()); + + let _ = vdom.rebuild(); + + let mut renderer = self.renderers.pull(pre_renderer); + + let mut html = String::new(); + + let result = write!( + &mut html, + r#" {head}

"# - ); + ); - if let Err(err) = result { - eprintln!("Failed to write to html: {}", err); - } + if let Err(err) = result { + eprintln!("Failed to write to html: {}", err); + } - let _ = renderer.render_to(&mut html, &vdom); + let _ = renderer.render_to(&mut html, &vdom); - if let Err(err) = write!( - &mut html, - r#"
+ if let Err(err) = write!( + &mut html, + r#"
"# - ) { - eprintln!("Failed to write to html: {}", err); - } - - html -} - -#[derive(Clone)] -pub struct SSRState { - // We keep a cache of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move - renderers: Arc>, -} - -impl Default for SSRState { - fn default() -> Self { - Self { - renderers: Arc::new(object_pool::Pool::new(10, pre_renderer)), + ) { + eprintln!("Failed to write to html: {}", err); } - } -} -impl SSRState { - pub fn render(&self, cfg: &ServeConfig

) -> String { - let mut renderer = self.renderers.pull(pre_renderer); - dioxus_ssr_html(cfg, &mut renderer) + html } } From 9877dd7ed8928a2c5d08b546a54c93b26204c287 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 2 Apr 2023 16:18:15 -0500 Subject: [PATCH 329/516] parse and inject pre rendered content to work with trunk --- packages/server/src/adapters/axum_adapter.rs | 48 ++++++++-- packages/server/src/adapters/salvo_adapter.rs | 36 +++++++- packages/server/src/adapters/warp_adapter.rs | 2 +- packages/server/src/render.rs | 39 +------- packages/server/src/serve.rs | 92 ++++++++++++------- 5 files changed, 132 insertions(+), 85 deletions(-) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 8fb98b6ec..75c736731 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -70,23 +70,51 @@ where } fn serve_dioxus_application( - self, + mut self, server_fn_route: &'static str, cfg: impl Into>, ) -> Self { - use tower_http::services::ServeDir; + use tower_http::services::{ServeDir, ServeFile}; let cfg = cfg.into(); - // Serve the dist folder and the index.html file - let serve_dir = ServeDir::new(cfg.assets_path); - - self.register_server_fns(server_fn_route) - .nest_service("/assets", serve_dir) - .route_service( - "/", - get(render_handler).with_state((cfg, SSRState::default())), + // Serve all files in dist folder except index.html + let dir = std::fs::read_dir(cfg.assets_path).unwrap_or_else(|e| { + panic!( + "Couldn't read assets directory at {:?}: {}", + &cfg.assets_path, e ) + }); + + for entry in dir.flatten() { + let path = entry.path(); + if path.ends_with("index.html") { + continue; + } + let route = path + .strip_prefix(&cfg.assets_path) + .unwrap() + .iter() + .map(|segment| { + segment.to_str().unwrap_or_else(|| { + panic!("Failed to convert path segment {:?} to string", segment) + }) + }) + .collect::>() + .join("/"); + let route = format!("/{}", route); + if path.is_dir() { + self = self.nest_service(&route, ServeDir::new(path)); + } else { + self = self.nest_service(&route, ServeFile::new(path)); + } + } + + // Add server functions and render index.html + self.register_server_fns(server_fn_route).route( + "/", + get(render_handler).with_state((cfg, SSRState::default())), + ) } } diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 0d8a27b22..f3bbd5f43 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -50,24 +50,50 @@ impl DioxusRouterExt for Router { } fn register_server_fns(self, server_fn_route: &'static str) -> Self { - self.register_server_fns_with_handler(|| ServerFnHandler { + self.register_server_fns_with_handler(server_fn_route, |func| ServerFnHandler { server_context: DioxusServerContext::default(), function: func, }) } fn serve_dioxus_application( - self, + mut self, server_fn_route: &'static str, cfg: impl Into>, ) -> Self { let cfg = cfg.into(); - // Serve the dist folder and the index.html file - let serve_dir = StaticDir::new([cfg.assets_path]); + + // Serve all files in dist folder except index.html + let dir = std::fs::read_dir(cfg.assets_path).unwrap_or_else(|e| { + panic!( + "Couldn't read assets directory at {:?}: {}", + &cfg.assets_path, e + ) + }); + + for entry in dir.flatten() { + let path = entry.path(); + if path.ends_with("index.html") { + continue; + } + let serve_dir = StaticDir::new([path.clone()]); + let route = path + .strip_prefix(&cfg.assets_path) + .unwrap() + .iter() + .map(|segment| { + segment.to_str().unwrap_or_else(|| { + panic!("Failed to convert path segment {:?} to string", segment) + }) + }) + .collect::>() + .join("/"); + let route = format!("/{}/<**path>", route); + self = self.push(Router::with_path(route).get(serve_dir)) + } self.register_server_fns(server_fn_route) .push(Router::with_path("/").get(SSRHandler { cfg })) - .push(Router::with_path("assets/<**path>").get(serve_dir)) } } diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 00ebb38fa..82d1c382f 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -69,7 +69,7 @@ pub fn serve_dioxus_application( .and(warp::get()) .and(with_ssr_state()) .map(move |renderer: SSRState| warp::reply::html(renderer.render(&cfg)))) - .or(warp::path("assets").and(serve_dir)) + .or(serve_dir) .boxed() } diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index bf8c12854..f6582d6a7 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -1,4 +1,3 @@ -use std::fmt::Write; use std::sync::Arc; use dioxus_core::VirtualDom; @@ -47,12 +46,7 @@ impl Default for SSRState { impl SSRState { pub fn render(&self, cfg: &ServeConfig

) -> String { let ServeConfig { - app, - application_name, - base_path, - head, - props, - .. + app, props, index, .. } = cfg; let mut vdom = VirtualDom::new_with_props(*app, props.clone()); @@ -63,38 +57,11 @@ impl SSRState { let mut html = String::new(); - let result = write!( - &mut html, - r#" - - - {head} - -

"# - ); - - if let Err(err) = result { - eprintln!("Failed to write to html: {}", err); - } + html += &index.pre_main; let _ = renderer.render_to(&mut html, &vdom); - if let Err(err) = write!( - &mut html, - r#"
- - - "# - ) { - eprintln!("Failed to write to html: {}", err); - } + html += &index.post_main; html } diff --git a/packages/server/src/serve.rs b/packages/server/src/serve.rs index ed059f47f..6284a6f1b 100644 --- a/packages/server/src/serve.rs +++ b/packages/server/src/serve.rs @@ -1,12 +1,15 @@ +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + use dioxus_core::Component; #[derive(Clone)] pub struct ServeConfigBuilder { pub(crate) app: Component

, pub(crate) props: P, - pub(crate) application_name: Option<&'static str>, - pub(crate) base_path: Option<&'static str>, - pub(crate) head: Option<&'static str>, + pub(crate) root_id: Option<&'static str>, + pub(crate) index_path: Option<&'static str>, pub(crate) assets_path: Option<&'static str>, } @@ -16,32 +19,25 @@ impl ServeConfigBuilder

{ Self { app, props, - application_name: None, - base_path: None, - head: None, + root_id: None, + index_path: None, assets_path: None, } } - /// Set the application name matching the name in the Dioxus.toml file used to build the application - pub fn application_name(mut self, application_name: &'static str) -> Self { - self.application_name = Some(application_name); + /// Set the path of the index.html file to be served. (defaults to {assets_path}/index.html) + pub fn index_path(mut self, index_path: &'static str) -> Self { + self.index_path = Some(index_path); self } - /// Set the path the WASM application will be served under - pub fn base_path(mut self, base_path: &'static str) -> Self { - self.base_path = Some(base_path); + /// Set the id of the root element in the index.html file to place the prerendered content into. (defaults to main) + pub fn root_id(mut self, root_id: &'static str) -> Self { + self.root_id = Some(root_id); self } - /// Set the head content to be included in the HTML document served - pub fn head(mut self, head: &'static str) -> Self { - self.head = Some(head); - self - } - - /// Set the path of the assets folder generated by the Dioxus CLI. (defaults to dist/assets) + /// Set the path of the assets folder generated by the Dioxus CLI. (defaults to dist) pub fn assets_path(mut self, assets_path: &'static str) -> Self { self.assets_path = Some(assets_path); self @@ -49,31 +45,61 @@ impl ServeConfigBuilder

{ /// Build the ServeConfig pub fn build(self) -> ServeConfig

{ - let base_path = self.base_path.unwrap_or("."); - let application_name = self.application_name.unwrap_or("dioxus"); + let assets_path = self.assets_path.unwrap_or("dist"); + + let index_path = self + .index_path + .map(PathBuf::from) + .unwrap_or_else(|| format!("{assets_path}/index.html").into()); + + let root_id = self.root_id.unwrap_or("main"); + + let index = load_index_html(index_path, root_id); + ServeConfig { app: self.app, props: self.props, - application_name, - base_path, - head: self.head.map(String::from).unwrap_or(format!(r#"Dioxus Application - - - - - "#)), - assets_path: self.assets_path.unwrap_or("dist/assets"), + index, + assets_path, } } } +fn load_index_html(path: PathBuf, root_id: &'static str) -> IndexHtml { + let mut file = File::open(path).expect("Failed to find index.html. Make sure the index_path is set correctly and the WASM application has been built."); + + let mut contents = String::new(); + file.read_to_string(&mut contents) + .expect("Failed to read index.html"); + + let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\"")).unwrap_or_else(|| panic!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page.")); + + let post_main = post_main.split_once('>').unwrap_or_else(|| { + panic!("Failed to find closing > after id=\"{root_id}\" in index.html.") + }); + + let (pre_main, post_main) = ( + pre_main.to_string() + &format!("id=\"{root_id}\"") + post_main.0 + ">", + post_main.1.to_string(), + ); + + IndexHtml { + pre_main, + post_main, + } +} + +#[derive(Clone)] +pub(crate) struct IndexHtml { + pub(crate) pre_main: String, + pub(crate) post_main: String, +} + #[derive(Clone)] pub struct ServeConfig { pub(crate) app: Component

, pub(crate) props: P, - pub(crate) application_name: &'static str, - pub(crate) base_path: &'static str, - pub(crate) head: String, + pub(crate) index: IndexHtml, pub(crate) assets_path: &'static str, } From 7214130c4040873c3f1ee17ded1d5afb8a01f6c7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 2 Apr 2023 17:45:28 -0500 Subject: [PATCH 330/516] hot reloading intigration --- packages/server/Cargo.toml | 10 ++- packages/server/src/adapters/axum_adapter.rs | 71 +++++++++++++-- packages/server/src/adapters/salvo_adapter.rs | 87 ++++++++++++++++++- packages/server/src/adapters/warp_adapter.rs | 69 ++++++++++++++- packages/server/src/hot_reload.rs | 46 ++++++++++ packages/server/src/lib.rs | 2 + packages/server/src/render.rs | 24 ----- 7 files changed, 274 insertions(+), 35 deletions(-) create mode 100644 packages/server/src/hot_reload.rs diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 086448ca1..563912754 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -15,13 +15,13 @@ warp = { version = "0.3.3", optional = true } http-body = { version = "0.4.5", optional = true } # axum -axum = { version = "0.6.1", optional = true } +axum = { version = "0.6.1", features = ["ws"], optional = true } tower-http = { version = "0.4.0", optional = true, features = ["fs"] } hyper = { version = "0.14.25", optional = true } axum-macros = "0.3.7" # salvo -salvo = { version = "0.37.7", optional = true, features = ["serve-static"] } +salvo = { version = "0.37.7", optional = true, features = ["serve-static", "ws"] } serde = "1.0.159" # Dioxus + SSR @@ -35,12 +35,16 @@ tokio = { version = "1.27.0", features = ["full"], optional = true } object-pool = "0.5.4" anymap = "0.12.1" +serde_json = { version = "1.0.95", optional = true } +tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } +futures-util = { version = "0.3.28", optional = true } + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] dioxus-hot-reload = { path = "../hot-reload" } [features] default = ["hot-reload"] -hot-reload = [] +hot-reload = ["serde_json", "tokio-stream", "futures-util"] warp = ["dep:warp", "http-body", "ssr"] axum = ["dep:axum", "tower-http", "hyper", "ssr"] salvo = ["dep:salvo", "hyper", "ssr"] diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 75c736731..9fbcd5893 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -2,7 +2,7 @@ use std::{error::Error, sync::Arc}; use axum::{ body::{self, Body, BoxBody, Full}, - extract::State, + extract::{State, WebSocketUpgrade}, handler::Handler, http::{HeaderMap, Request, Response, StatusCode}, response::IntoResponse, @@ -36,6 +36,8 @@ pub trait DioxusRouterExt { server_fn_route: &'static str, cfg: impl Into>, ) -> Self; + + fn connect_hot_reload(self) -> Self; } impl DioxusRouterExt for Router @@ -92,7 +94,7 @@ where continue; } let route = path - .strip_prefix(&cfg.assets_path) + .strip_prefix(cfg.assets_path) .unwrap() .iter() .map(|segment| { @@ -111,10 +113,26 @@ where } // Add server functions and render index.html - self.register_server_fns(server_fn_route).route( - "/", - get(render_handler).with_state((cfg, SSRState::default())), - ) + self.connect_hot_reload() + .register_server_fns(server_fn_route) + .route( + "/", + get(render_handler).with_state((cfg, SSRState::default())), + ) + } + + fn connect_hot_reload(self) -> Self { + #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] + { + self.route( + "/_dioxus/hot_reload", + get(hot_reload_handler).with_state(crate::hot_reload::HotReloadState::default()), + ) + } + #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] + { + self + } } } @@ -195,3 +213,44 @@ fn report_err(e: E) -> Response { .body(body::boxed(format!("Error: {}", e))) .unwrap() } + +#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] +pub async fn hot_reload_handler( + ws: WebSocketUpgrade, + State(state): State, +) -> impl IntoResponse { + use axum::extract::ws::Message; + use futures_util::StreamExt; + + ws.on_upgrade(|mut socket| async move { + println!("🔥 Hot Reload WebSocket connected"); + { + // update any rsx calls that changed before the websocket connected. + { + println!("🔮 Finding updates since last compile..."); + let templates_read = state.templates.read().await; + + for template in &*templates_read { + if socket + .send(Message::Text(serde_json::to_string(&template).unwrap())) + .await + .is_err() + { + return; + } + } + } + println!("finished"); + } + + let mut rx = tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver); + while let Some(change) = rx.next().await { + if let Some(template) = change { + let template = { serde_json::to_string(&template).unwrap() }; + if socket.send(Message::Text(template)).await.is_err() { + break; + }; + } + } + }) +} diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index f3bbd5f43..24aab9643 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -17,6 +17,7 @@ use crate::{ pub trait DioxusRouterExt { fn register_server_fns(self, server_fn_route: &'static str) -> Self; + fn register_server_fns_with_handler( self, server_fn_route: &'static str, @@ -24,11 +25,14 @@ pub trait DioxusRouterExt { ) -> Self where H: Handler + 'static; + fn serve_dioxus_application( self, server_fn_path: &'static str, cfg: impl Into>, ) -> Self; + + fn connect_hot_reload(self) -> Self; } impl DioxusRouterExt for Router { @@ -92,9 +96,14 @@ impl DioxusRouterExt for Router { self = self.push(Router::with_path(route).get(serve_dir)) } - self.register_server_fns(server_fn_route) + self.connect_hot_reload() + .register_server_fns(server_fn_route) .push(Router::with_path("/").get(SSRHandler { cfg })) } + + fn connect_hot_reload(self) -> Self { + self.push(Router::with_path("/_dioxus/hot_reload").get(HotReloadHandler::default())) + } } struct SSRHandler { @@ -217,3 +226,79 @@ fn handle_error(error: impl Error + Send + Sync, res: &mut Response) { resp_err.render(format!("Internal Server Error: {}", error)); *res = resp_err; } + +#[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] +#[derive(Default)] +pub struct HotReloadHandler; + +#[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] +#[handler] +impl HotReloadHandler { + async fn handle( + &self, + _req: &mut Request, + _depot: &mut Depot, + _res: &mut Response, + ) -> Result<(), salvo::http::StatusError> { + Err(salvo::http::StatusError::not_found()) + } +} + +#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] +#[derive(Default)] +pub struct HotReloadHandler { + state: crate::hot_reload::HotReloadState, +} + +#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] +#[handler] +impl HotReloadHandler { + async fn handle( + &self, + req: &mut Request, + _depot: &mut Depot, + res: &mut Response, + ) -> Result<(), salvo::http::StatusError> { + use salvo::ws::Message; + use salvo::ws::WebSocketUpgrade; + + let state = self.state.clone(); + + WebSocketUpgrade::new() + .upgrade(req, res, |mut websocket| async move { + use futures_util::StreamExt; + + println!("🔥 Hot Reload WebSocket connected"); + { + // update any rsx calls that changed before the websocket connected. + { + println!("🔮 Finding updates since last compile..."); + let templates_read = state.templates.read().await; + + for template in &*templates_read { + if websocket + .send(Message::text(serde_json::to_string(&template).unwrap())) + .await + .is_err() + { + return; + } + } + } + println!("finished"); + } + + let mut rx = + tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver); + while let Some(change) = rx.next().await { + if let Some(template) = change { + let template = { serde_json::to_string(&template).unwrap() }; + if websocket.send(Message::text(template)).await.is_err() { + break; + }; + } + } + }) + .await + } +} diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 82d1c382f..67c3c75e4 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -64,7 +64,8 @@ pub fn serve_dioxus_application( // Serve the dist folder and the index.html file let serve_dir = warp::fs::dir(cfg.assets_path); - register_server_fns(server_fn_route) + connect_hot_reload() + .or(register_server_fns(server_fn_route)) .or(warp::path::end() .and(warp::get()) .and(with_ssr_state()) @@ -159,3 +160,69 @@ fn report_err(e: E) -> Box { .unwrap(), ) as Box } + +pub fn connect_hot_reload() -> impl Filter { + #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] + { + warp::path("_dioxus/hot_reload").and(warp::ws()).map(|| { + Response::builder() + .status(StatusCode::NOT_FOUND) + .body("Not Found".into()) + .unwrap() + }) + } + #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] + { + use crate::hot_reload::HotReloadState; + let state = HotReloadState::default(); + + warp::path("_dioxus") + .and(warp::path("hot_reload")) + .and(warp::ws()) + .and(warp::any().map(move || state.clone())) + .map(move |ws: warp::ws::Ws, state: HotReloadState| { + #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] + ws.on_upgrade(move |mut websocket| { + async move { + use futures_util::sink::SinkExt; + use futures_util::StreamExt; + use warp::ws::Message; + + println!("🔥 Hot Reload WebSocket connected"); + { + // update any rsx calls that changed before the websocket connected. + { + println!("🔮 Finding updates since last compile..."); + let templates_read = state.templates.read().await; + + for template in &*templates_read { + if websocket + .send(Message::text( + serde_json::to_string(&template).unwrap(), + )) + .await + .is_err() + { + return; + } + } + } + println!("finished"); + } + + let mut rx = tokio_stream::wrappers::WatchStream::from_changes( + state.message_receiver, + ); + while let Some(change) = rx.next().await { + if let Some(template) = change { + let template = { serde_json::to_string(&template).unwrap() }; + if websocket.send(Message::text(template)).await.is_err() { + break; + }; + } + } + } + }) + }) + } +} diff --git a/packages/server/src/hot_reload.rs b/packages/server/src/hot_reload.rs new file mode 100644 index 000000000..c4f89e704 --- /dev/null +++ b/packages/server/src/hot_reload.rs @@ -0,0 +1,46 @@ +use std::sync::Arc; + +use dioxus_core::Template; +use tokio::sync::{ + watch::{channel, Receiver}, + RwLock, +}; + +#[derive(Clone)] +pub struct HotReloadState { + // The cache of all templates that have been modified since the last time we checked + pub(crate) templates: Arc>>>, + // The channel to send messages to the hot reload thread + pub(crate) message_receiver: Receiver>>, +} + +impl Default for HotReloadState { + fn default() -> Self { + let templates = Arc::new(RwLock::new(std::collections::HashSet::new())); + let (tx, rx) = channel(None); + + dioxus_hot_reload::connect({ + let templates = templates.clone(); + move |msg| match msg { + dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => { + { + let mut templates = templates.blocking_write(); + templates.insert(template); + } + + if let Err(err) = tx.send(Some(template)) { + log::error!("Failed to send hot reload message: {}", err); + } + } + dioxus_hot_reload::HotReloadMsg::Shutdown => { + std::process::exit(0); + } + } + }); + + Self { + templates, + message_receiver: rx, + } + } +} diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index a9c8e4939..5e395fa70 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -2,6 +2,8 @@ use dioxus_core::prelude::*; mod adapters; +#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] +mod hot_reload; #[cfg(feature = "ssr")] pub mod render; #[cfg(feature = "ssr")] diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index f6582d6a7..1f66067fd 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -9,36 +9,12 @@ use crate::prelude::ServeConfig; pub struct SSRState { // We keep a cache of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move renderers: Arc>, - #[cfg(all(debug_assertions, feature = "hot-reload"))] - // The cache of all templates that have been modified since the last time we checked - templates: Arc>>>, } impl Default for SSRState { fn default() -> Self { - #[cfg(all(debug_assertions, feature = "hot-reload"))] - let templates = { - let templates = Arc::new(std::sync::RwLock::new(std::collections::HashSet::new())); - dioxus_hot_reload::connect({ - let templates = templates.clone(); - move |msg| match msg { - dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => { - if let Ok(mut templates) = templates.write() { - templates.insert(template); - } - } - dioxus_hot_reload::HotReloadMsg::Shutdown => { - std::process::exit(0); - } - } - }); - templates - }; - Self { renderers: Arc::new(object_pool::Pool::new(10, pre_renderer)), - #[cfg(all(debug_assertions, feature = "hot-reload"))] - templates, } } } From 987a0d5532c721675789334cec46994cdc0f5433 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 2 Apr 2023 18:13:27 -0500 Subject: [PATCH 331/516] fix salvo serving static files --- packages/server/src/adapters/salvo_adapter.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 24aab9643..357e1c19c 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -2,8 +2,9 @@ use std::{error::Error, sync::Arc}; use hyper::{http::HeaderValue, StatusCode}; use salvo::{ - async_trait, handler, serve_static::StaticDir, Depot, FlowCtrl, Handler, Request, Response, - Router, + async_trait, handler, + serve_static::{StaticDir, StaticFile}, + Depot, FlowCtrl, Handler, Request, Response, Router, }; use server_fn::{Payload, ServerFunctionRegistry}; use tokio::task::spawn_blocking; @@ -80,7 +81,6 @@ impl DioxusRouterExt for Router { if path.ends_with("index.html") { continue; } - let serve_dir = StaticDir::new([path.clone()]); let route = path .strip_prefix(&cfg.assets_path) .unwrap() @@ -92,8 +92,15 @@ impl DioxusRouterExt for Router { }) .collect::>() .join("/"); - let route = format!("/{}/<**path>", route); - self = self.push(Router::with_path(route).get(serve_dir)) + if path.is_file() { + let route = format!("/{}", route); + let serve_dir = StaticFile::new(path.clone()); + self = self.push(Router::with_path(route).get(serve_dir)) + } else { + let route = format!("/{}/<**path>", route); + let serve_dir = StaticDir::new([path.clone()]); + self = self.push(Router::with_path(route).get(serve_dir)) + } } self.connect_hot_reload() From d05c85db31d7e592bf91cc68589e2548b1767245 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 08:09:22 -0500 Subject: [PATCH 332/516] Document common items --- packages/server/src/adapters/mod.rs | 12 ++++ packages/server/src/lib.rs | 72 +++++++++++++++++-- packages/server/src/render.rs | 7 +- .../server/src/{serve.rs => serve_config.rs} | 7 +- packages/server/src/server_context.rs | 39 +++++++++- packages/server/src/server_fn.rs | 10 ++- 6 files changed, 132 insertions(+), 15 deletions(-) rename packages/server/src/{serve.rs => serve_config.rs} (85%) diff --git a/packages/server/src/adapters/mod.rs b/packages/server/src/adapters/mod.rs index 0c292bd8d..148062db0 100644 --- a/packages/server/src/adapters/mod.rs +++ b/packages/server/src/adapters/mod.rs @@ -1,3 +1,15 @@ +//! # Adapters +//! Adapters for different web frameworks. +//! +//! Each adapter provides a set of utilities that is ergonomic to use with the framework. +//! +//! Each framework has utilies for some or all of the following: +//! - Server functions +//! - A generic way to register server functions +//! - A way to register server functions with a custom handler that allows users to pass in a custom [`DioxusServerContext`] based on the state of the server framework. +//! - A way to register static WASM files that is accepts [`ServeConfig`] +//! - A hot reloading web socket that intigrates with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload) + #[cfg(feature = "axum")] pub mod axum_adapter; #[cfg(feature = "salvo")] diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 5e395fa70..9654adebe 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -1,3 +1,66 @@ +//! Fullstack utilities for the [`dioxus`](https://dioxuslabs.com) framework. +//! +//! # Features +//! - Intigrations with the [axum](crate::adapters::axum_adapter), [salvo](crate::adapters::salvo_adapters), and [warp](crate::adapters::warp_adapters) server frameworks with utilities for serving and rendering Dioxus applications. +//! - Server functions that allow you to call code on the server from the client as if it were a normal function. +//! - Instant RSX Hot reloading with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload). +//! +//! # Example +//! ```rust +//! #![allow(non_snake_case)] +//! use dioxus::prelude::*; +//! use dioxus_server::prelude::*; +//! +//! fn main() { +//! #[cfg(feature = "web")] +//! // Hydrate the application on the client +//! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); +//! #[cfg(feature = "ssr")] +//! { +//! GetServerData::register().unwrap(); +//! tokio::runtime::Runtime::new() +//! .unwrap() +//! .block_on(async move { +//! let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); +//! axum::Server::bind(&addr) +//! .serve( +//! axum::Router::new() +//! // Server side render the application, serve static assets, and register server functions +//! .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) +//! .into_make_service(), +//! ) +//! .await +//! .unwrap(); +//! }); +//! } +//! } +//! +//! fn app(cx: Scope) -> Element { +//! let text = use_state(cx, || "...".to_string()); +//! +//! cx.render(rsx! { +//! button { +//! onclick: move |_| { +//! to_owned![text]; +//! async move { +//! if let Ok(data) = get_server_data().await { +//! text.set(data.clone()); +//! } +//! } +//! }, +//! "Run a server function" +//! } +//! "Server said: {text}" +//! }) +//! } +//! +//! #[server(GetServerData)] +//! async fn get_server_data() -> Result { +//! Ok("Hello from the server!".to_string()) +//! } +//! ``` + +#![warn(missing_docs)] #[allow(unused)] use dioxus_core::prelude::*; @@ -5,12 +68,13 @@ mod adapters; #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] mod hot_reload; #[cfg(feature = "ssr")] -pub mod render; +mod render; #[cfg(feature = "ssr")] -mod serve; +mod serve_config; mod server_context; mod server_fn; +/// A prelude of commonly used items in dioxus-server. pub mod prelude { #[cfg(feature = "axum")] pub use crate::adapters::axum_adapter::*; @@ -19,9 +83,7 @@ pub mod prelude { #[cfg(feature = "warp")] pub use crate::adapters::warp_adapter::*; #[cfg(feature = "ssr")] - pub use crate::render::*; - #[cfg(feature = "ssr")] - pub use crate::serve::{ServeConfig, ServeConfigBuilder}; + pub use crate::serve_config::{ServeConfig, ServeConfigBuilder}; pub use crate::server_context::DioxusServerContext; pub use crate::server_fn::ServerFn; #[cfg(feature = "ssr")] diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 1f66067fd..22e751ea8 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -1,3 +1,5 @@ +//! A shared pool of renderers for efficient server side rendering. + use std::sync::Arc; use dioxus_core::VirtualDom; @@ -5,9 +7,10 @@ use dioxus_ssr::Renderer; use crate::prelude::ServeConfig; +/// State used in server side rendering. This utilizes a pool of [`dioxus_ssr::Renderer`]s to cache static templates between renders. #[derive(Clone)] -pub struct SSRState { - // We keep a cache of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move +pub(crate) struct SSRState { + // We keep a pool of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move renderers: Arc>, } diff --git a/packages/server/src/serve.rs b/packages/server/src/serve_config.rs similarity index 85% rename from packages/server/src/serve.rs rename to packages/server/src/serve_config.rs index 6284a6f1b..b7b29e9fb 100644 --- a/packages/server/src/serve.rs +++ b/packages/server/src/serve_config.rs @@ -1,9 +1,12 @@ +//! Configeration for how to serve a Dioxus application + use std::fs::File; use std::io::Read; use std::path::PathBuf; use dioxus_core::Component; +/// A ServeConfig is used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus-ssr`]. #[derive(Clone)] pub struct ServeConfigBuilder { pub(crate) app: Component

, @@ -14,7 +17,7 @@ pub struct ServeConfigBuilder { } impl ServeConfigBuilder

{ - /// Create a new ServeConfig + /// Create a new ServeConfigBuilder with the root component and props to render on the server. pub fn new(app: Component

, props: P) -> Self { Self { app, @@ -95,6 +98,8 @@ pub(crate) struct IndexHtml { pub(crate) post_main: String, } +/// Used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus-ssr`]. +/// See [`ServeConfigBuilder`] to create a ServeConfig #[derive(Clone)] pub struct ServeConfig { pub(crate) app: Component

, diff --git a/packages/server/src/server_context.rs b/packages/server/src/server_context.rs index 427ee21f8..7e2a494c5 100644 --- a/packages/server/src/server_context.rs +++ b/packages/server/src/server_context.rs @@ -4,7 +4,8 @@ use anymap::{any::Any, Map}; type SendSyncAnyMap = Map; -/// A shared context for server functions. This allows you to pass data between your server and the server functions like authentication session data. +/// A shared context for server functions. +/// This allows you to pass data between your server framework and the server functions. This can be used to pass request information or information about the state of the server. For example, you could pass authentication data though this context to your server functions. #[derive(Clone)] pub struct DioxusServerContext { shared_context: Arc>, @@ -19,10 +20,12 @@ impl Default for DioxusServerContext { } impl DioxusServerContext { + /// Clone a value from the shared server context pub fn get(&self) -> Option { self.shared_context.read().ok()?.get::().cloned() } + /// Insert a value into the shared server context pub fn insert( &mut self, value: T, @@ -33,3 +36,37 @@ impl DioxusServerContext { .map(|_| ()) } } + +/// Generate a server context from a tuple of values +macro_rules! server_context { + ($({$(($name:ident: $ty:ident)),*}),*) => { + $( + #[allow(unused_mut)] + impl< $($ty: Send + Sync + 'static),* > From<($($ty,)*)> for $crate::server_context::DioxusServerContext { + fn from(( $($name,)* ): ($($ty,)*)) -> Self { + let mut context = $crate::server_context::DioxusServerContext::default(); + $(context.insert::<$ty>($name).unwrap();)* + context + } + } + )* + }; +} + +server_context!( + {}, + {(a: A)}, + {(a: A), (b: B)}, + {(a: A), (b: B), (c: C)}, + {(a: A), (b: B), (c: C), (d: D)}, + {(a: A), (b: B), (c: C), (d: D), (e: E)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K), (l: L)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K), (l: L), (m: M)}, + {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K), (l: L), (m: M), (n: N)} +); diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs index 7170d61de..6f985a39a 100644 --- a/packages/server/src/server_fn.rs +++ b/packages/server/src/server_fn.rs @@ -1,6 +1,7 @@ use crate::server_context::DioxusServerContext; #[cfg(any(feature = "ssr", doc))] +/// A trait object for a function that be called on serializable arguments and returns a serializable result. pub type ServerFnTraitObj = server_fn::ServerFnTraitObj; #[cfg(any(feature = "ssr", doc))] @@ -78,17 +79,14 @@ pub enum ServerRegistrationFnError { /// Defines a "server function." A server function can be called from the server or the client, /// but the body of its code will only be run on the server, i.e., if a crate feature `ssr` is enabled. /// -/// (This follows the same convention as the Dioxus framework's distinction between `ssr` for server-side rendering, -/// and `csr` and `hydrate` for client-side rendering and hydration, respectively.) -/// /// Server functions are created using the `server` macro. /// /// The function should be registered by calling `ServerFn::register()`. The set of server functions -/// can be queried on the server for routing purposes by calling [server_fn_by_path]. +/// can be queried on the server for routing purposes by calling [ServerFunctionRegistry::get]. /// -/// Technically, the trait is implemented on a type that describes the server function's arguments. +/// Technically, the trait is implemented on a type that describes the server function's arguments, not the function itself. pub trait ServerFn: server_fn::ServerFn { - /// Registers the server function, allowing the server to query it by URL. + /// Registers the server function, allowing the client to query it by URL. #[cfg(any(feature = "ssr", doc))] fn register() -> Result<(), server_fn::ServerFnError> { Self::register_in::() From 9d5d64737803a4b2e31e55a13fe10e1204bf594d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 08:09:30 -0500 Subject: [PATCH 333/516] document axum adapter --- packages/server/src/adapters/axum_adapter.rs | 179 ++++++++++++++++++- 1 file changed, 175 insertions(+), 4 deletions(-) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 9fbcd5893..ad3fafd6a 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -1,3 +1,60 @@ +//! Dioxus utilities for the [Axum](https://docs.rs/axum/latest/axum/index.html) server framework. +//! +//! # Example +//! ```rust +//! # #![allow(non_snake_case)] +//! # use dioxus::prelude::*; +//! # use dioxus_server::prelude::*; +//! +//! fn main() { +//! #[cfg(feature = "web")] +//! // Hydrate the application on the client +//! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); +//! #[cfg(feature = "ssr")] +//! { +//! GetServerData::register().unwrap(); +//! tokio::runtime::Runtime::new() +//! .unwrap() +//! .block_on(async move { +//! let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); +//! axum::Server::bind(&addr) +//! .serve( +//! axum::Router::new() +//! // Server side render the application, serve static assets, and register server functions +//! .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) +//! .into_make_service(), +//! ) +//! .await +//! .unwrap(); +//! }); +//! } +//! } +//! +//! fn app(cx: Scope) -> Element { +//! let text = use_state(cx, || "...".to_string()); +//! +//! cx.render(rsx! { +//! button { +//! onclick: move |_| { +//! to_owned![text]; +//! async move { +//! if let Ok(data) = get_server_data().await { +//! text.set(data.clone()); +//! } +//! } +//! }, +//! "Run a server function" +//! } +//! "Server said: {text}" +//! }) +//! } +//! +//! #[server(GetServerData)] +//! async fn get_server_data() -> Result { +//! Ok("Hello from the server!".to_string()) +//! } +//! ``` + use std::{error::Error, sync::Arc}; use axum::{ @@ -14,12 +71,41 @@ use tokio::task::spawn_blocking; use crate::{ render::SSRState, - serve::ServeConfig, + serve_config::ServeConfig, server_context::DioxusServerContext, server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, }; +/// A extension trait with utilities for integrating Dioxus with your Axum router. pub trait DioxusRouterExt { + /// Registers server functions with a custom handler function. This allows you to pass custom context to your server functions by generating a [`DioxusServerContext`] from the request. + /// + /// # Example + /// ```rust + /// # use dioxus::prelude::*; + /// # use dioxus_server::prelude::*; + /// + /// fn main() { + /// tokio::runtime::Runtime::new() + /// .unwrap() + /// .block_on(async move { + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// .register_server_fns_with_handler("", |func| { + /// move |headers: HeaderMap, body: Request| async move { + /// // Add the headers to the context + /// server_fn_handler((headers.clone(),).into(), func.clone(), headers, body).await + /// } + /// }) + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); + /// }); + /// } + /// ``` fn register_server_fns_with_handler( self, server_fn_route: &'static str, @@ -29,15 +115,98 @@ pub trait DioxusRouterExt { H: Handler, T: 'static, S: Clone + Send + Sync + 'static; + + /// Registers server functions with the default handler. This handler function will pass an empty [`DioxusServerContext`] to your server functions. + /// + /// # Example + /// ```rust + /// # use dioxus::prelude::*; + /// # use dioxus_server::prelude::*; + /// + /// fn main() { + /// tokio::runtime::Runtime::new() + /// .unwrap() + /// .block_on(async move { + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// .register_server_fns("") + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); + /// }); + /// } + /// ``` fn register_server_fns(self, server_fn_route: &'static str) -> Self; + /// Register the web RSX hot reloading endpoint. This will enable hot reloading for your application in debug mode when you call [`dioxus_hot_reload::hot_reload_init`]. + /// + /// # Example + /// /// # Example + /// ```rust + /// # #![allow(non_snake_case)] + /// # use dioxus::prelude::*; + /// # use dioxus_server::prelude::*; + /// + /// fn main() { + /// GetServerData::register().unwrap(); + /// tokio::runtime::Runtime::new() + /// .unwrap() + /// .block_on(async move { + /// hot_reload_init!(); + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// // Server side render the application, serve static assets, and register server functions + /// .connect_hot_reload() + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); + /// }); + /// } + /// + /// # fn app(cx: Scope) -> Element {todo!()} + /// ``` + fn connect_hot_reload(self) -> Self; + + /// Serves the Dioxus application. This will serve a complete server side rendered application. + /// It will serve static assets, server render the application, register server functions, and intigrate with hot reloading. + /// + /// # Example + /// ```rust + /// # #![allow(non_snake_case)] + /// # use dioxus::prelude::*; + /// # use dioxus_server::prelude::*; + /// + /// fn main() { + /// GetServerData::register().unwrap(); + /// tokio::runtime::Runtime::new() + /// .unwrap() + /// .block_on(async move { + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// // Server side render the application, serve static assets, and register server functions + /// .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); + /// }); + /// } + /// + /// # fn app(cx: Scope) -> Element {todo!()} + /// ``` fn serve_dioxus_application( self, server_fn_route: &'static str, cfg: impl Into>, ) -> Self; - - fn connect_hot_reload(self) -> Self; } impl DioxusRouterExt for Router @@ -66,7 +235,7 @@ where fn register_server_fns(self, server_fn_route: &'static str) -> Self { self.register_server_fns_with_handler(server_fn_route, |func| { move |headers: HeaderMap, body: Request| async move { - server_fn_handler(DioxusServerContext::default(), func.clone(), headers, body).await + server_fn_handler(().into(), func.clone(), headers, body).await } }) } @@ -143,6 +312,7 @@ async fn render_handler( Full::from(rendered) } +/// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: DioxusServerContext, function: Arc, @@ -214,6 +384,7 @@ fn report_err(e: E) -> Response { .unwrap() } +/// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change. #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] pub async fn hot_reload_handler( ws: WebSocketUpgrade, From 556e4f374b1cf226e4302e4150a381990118a512 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 11:59:40 -0500 Subject: [PATCH 334/516] Document salvo adapter --- packages/server/src/adapters/axum_adapter.rs | 127 ++++++------- packages/server/src/adapters/salvo_adapter.rs | 167 +++++++++++++++++- 2 files changed, 217 insertions(+), 77 deletions(-) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index ad3fafd6a..34e6a5e46 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -39,7 +39,7 @@ //! to_owned![text]; //! async move { //! if let Ok(data) = get_server_data().await { -//! text.set(data.clone()); +//! text.set(data); //! } //! } //! }, @@ -85,25 +85,22 @@ pub trait DioxusRouterExt { /// # use dioxus::prelude::*; /// # use dioxus_server::prelude::*; /// - /// fn main() { - /// tokio::runtime::Runtime::new() - /// .unwrap() - /// .block_on(async move { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - /// axum::Server::bind(&addr) - /// .serve( - /// axum::Router::new() - /// .register_server_fns_with_handler("", |func| { - /// move |headers: HeaderMap, body: Request| async move { - /// // Add the headers to the context - /// server_fn_handler((headers.clone(),).into(), func.clone(), headers, body).await - /// } - /// }) - /// .into_make_service(), - /// ) - /// .await - /// .unwrap(); - /// }); + /// #[tokio::main] + /// async fn main() { + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// .register_server_fns_with_handler("", |func| { + /// move |headers: HeaderMap, body: Request| async move { + /// // Add the headers to the context + /// server_fn_handler((headers.clone(),), func.clone(), headers, body).await + /// } + /// }) + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); /// } /// ``` fn register_server_fns_with_handler( @@ -123,20 +120,17 @@ pub trait DioxusRouterExt { /// # use dioxus::prelude::*; /// # use dioxus_server::prelude::*; /// - /// fn main() { - /// tokio::runtime::Runtime::new() - /// .unwrap() - /// .block_on(async move { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - /// axum::Server::bind(&addr) - /// .serve( - /// axum::Router::new() - /// .register_server_fns("") - /// .into_make_service(), - /// ) - /// .await - /// .unwrap(); - /// }); + /// #[tokio::main] + /// async fn main() { + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// .register_server_fns("") + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); /// } /// ``` fn register_server_fns(self, server_fn_route: &'static str) -> Self; @@ -150,23 +144,19 @@ pub trait DioxusRouterExt { /// # use dioxus::prelude::*; /// # use dioxus_server::prelude::*; /// - /// fn main() { - /// GetServerData::register().unwrap(); - /// tokio::runtime::Runtime::new() - /// .unwrap() - /// .block_on(async move { - /// hot_reload_init!(); - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - /// axum::Server::bind(&addr) - /// .serve( - /// axum::Router::new() - /// // Server side render the application, serve static assets, and register server functions - /// .connect_hot_reload() - /// .into_make_service(), - /// ) - /// .await - /// .unwrap(); - /// }); + /// #[tokio::main] + /// async fn main() { + /// hot_reload_init!(); + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// // Server side render the application, serve static assets, and register server functions + /// .connect_hot_reload() + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); /// } /// /// # fn app(cx: Scope) -> Element {todo!()} @@ -174,7 +164,7 @@ pub trait DioxusRouterExt { fn connect_hot_reload(self) -> Self; /// Serves the Dioxus application. This will serve a complete server side rendered application. - /// It will serve static assets, server render the application, register server functions, and intigrate with hot reloading. + /// This will serve static assets, server render the application, register server functions, and intigrate with hot reloading. /// /// # Example /// ```rust @@ -182,22 +172,18 @@ pub trait DioxusRouterExt { /// # use dioxus::prelude::*; /// # use dioxus_server::prelude::*; /// - /// fn main() { - /// GetServerData::register().unwrap(); - /// tokio::runtime::Runtime::new() - /// .unwrap() - /// .block_on(async move { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - /// axum::Server::bind(&addr) - /// .serve( - /// axum::Router::new() - /// // Server side render the application, serve static assets, and register server functions - /// .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) - /// .into_make_service(), - /// ) - /// .await - /// .unwrap(); - /// }); + /// #[tokio::main] + /// async fn main() { + /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// axum::Server::bind(&addr) + /// .serve( + /// axum::Router::new() + /// // Server side render the application, serve static assets, and register server functions + /// .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + /// .into_make_service(), + /// ) + /// .await + /// .unwrap(); /// } /// /// # fn app(cx: Scope) -> Element {todo!()} @@ -235,7 +221,7 @@ where fn register_server_fns(self, server_fn_route: &'static str) -> Self { self.register_server_fns_with_handler(server_fn_route, |func| { move |headers: HeaderMap, body: Request| async move { - server_fn_handler(().into(), func.clone(), headers, body).await + server_fn_handler((), func.clone(), headers, body).await } }) } @@ -314,11 +300,12 @@ async fn render_handler( /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( - server_context: DioxusServerContext, + server_context: impl Into, function: Arc, headers: HeaderMap, req: Request, ) -> impl IntoResponse { + let server_context = server_context.into(); let (_, body) = req.into_parts(); let body = hyper::body::to_bytes(body).await; let Ok(body)=body else { diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 357e1c19c..6bc8947a6 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -1,3 +1,54 @@ +//! Dioxus utilities for the [Salvo](https://salvo.rs) server framework. +//! +//! # Example +//! ```rust +//! # #![allow(non_snake_case)] +//! # use dioxus::prelude::*; +//! # use dioxus_server::prelude::*; +//! +//! fn main() { +//! #[cfg(feature = "web")] +//! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); +//! #[cfg(feature = "ssr")] +//! { +//! use salvo::prelude::*; +//! GetServerData::register().unwrap(); +//! tokio::runtime::Runtime::new() +//! .unwrap() +//! .block_on(async move { +//! let router = +//! Router::new().serve_dioxus_application("", ServeConfigBuilder::new(app, ())); +//! Server::new(TcpListener::bind("127.0.0.1:8080")) +//! .serve(router) +//! .await; +//! }); +//! } +//! } +//! +//! fn app(cx: Scope) -> Element { +//! let text = use_state(cx, || "...".to_string()); +//! +//! cx.render(rsx! { +//! button { +//! onclick: move |_| { +//! to_owned![text]; +//! async move { +//! if let Ok(data) = get_server_data().await { +//! text.set(data); +//! } +//! } +//! }, +//! "Run a server function" +//! } +//! "Server said: {text}" +//! }) +//! } +//! +//! #[server(GetServerData)] +//! async fn get_server_data() -> Result { +//! Ok("Hello from the server!".to_string()) +//! } + use std::{error::Error, sync::Arc}; use hyper::{http::HeaderValue, StatusCode}; @@ -11,14 +62,51 @@ use tokio::task::spawn_blocking; use crate::{ prelude::DioxusServerContext, - prelude::SSRState, - serve::ServeConfig, + render::SSRState, + serve_config::ServeConfig, server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, }; +/// A extension trait with utilities for integrating Dioxus with your Salvo router. pub trait DioxusRouterExt { - fn register_server_fns(self, server_fn_route: &'static str) -> Self; - + /// Registers server functions with a custom handler function. This allows you to pass custom context to your server functions by generating a [`DioxusServerContext`] from the request. + /// + /// ```rust + /// use salvo::prelude::*; + /// use std::{net::TcpListener, sync::Arc}; + /// use dioxus_server::prelude::*; + /// + /// struct ServerFunctionHandler { + /// server_fn: Arc, + /// } + /// + /// #[handler] + /// impl ServerFunctionHandler { + /// async fn handle( + /// &self, + /// req: &mut Request, + /// depot: &mut Depot, + /// res: &mut Response, + /// flow: &mut FlowCtrl, + /// ) { + /// // Add the headers to server context + /// ServerFnHandler::new((req.headers().clone(),), self.server_fn.clone()) + /// .handle(req, depot, res, flow) + /// .await + /// } + /// } + /// + /// #[tokio::main] + /// async fn main() { + /// let router = Router::new() + /// .register_server_fns_with_handler("", |func| { + /// ServerFnHandler::new(DioxusServerContext::default(), func) + /// }); + /// Server::new(TcpListener::bind("127.0.0.1:8080")) + /// .serve(router) + /// .await; + /// } + /// ``` fn register_server_fns_with_handler( self, server_fn_route: &'static str, @@ -27,13 +115,70 @@ pub trait DioxusRouterExt { where H: Handler + 'static; + /// Registers server functions with the default handler. This handler function will pass an empty [`DioxusServerContext`] to your server functions. + /// + /// # Example + /// ```rust + /// use salvo::prelude::*; + /// use std::{net::TcpListener, sync::Arc}; + /// use dioxus_server::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let router = Router::new() + /// .register_server_fns(""); + /// Server::new(TcpListener::bind("127.0.0.1:8080")) + /// .serve(router) + /// .await; + /// } + /// + /// ``` + fn register_server_fns(self, server_fn_route: &'static str) -> Self; + + /// Register the web RSX hot reloading endpoint. This will enable hot reloading for your application in debug mode when you call [`dioxus_hot_reload::hot_reload_init`]. + /// + /// # Example + /// ```rust + /// use salvo::prelude::*; + /// use std::{net::TcpListener, sync::Arc}; + /// use dioxus_server::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let router = Router::new() + /// .connect_hot_reload(); + /// Server::new(TcpListener::bind("127.0.0.1:8080")) + /// .serve(router) + /// .await; + /// } + fn connect_hot_reload(self) -> Self; + + /// Serves the Dioxus application. This will serve a complete server side rendered application. + /// This will serve static assets, server render the application, register server functions, and intigrate with hot reloading. + /// + /// # Example + /// ```rust + /// #![allow(non_snake_case)] + /// use dioxus::prelude::*; + /// use dioxus_server::prelude::*; + /// use salvo::prelude::*; + /// use std::{net::TcpListener, sync::Arc}; + /// + /// #[tokio::main] + /// async fn main() { + /// let router = Router::new().serve_dioxus_application("", ServeConfigBuilder::new(app, ())); + /// Server::new(TcpListener::bind("127.0.0.1:8080")) + /// .serve(router) + /// .await; + /// } + /// + /// # fn app(cx: Scope) -> Element {todo!()} + /// ``` fn serve_dioxus_application( self, server_fn_path: &'static str, cfg: impl Into>, ) -> Self; - - fn connect_hot_reload(self) -> Self; } impl DioxusRouterExt for Router { @@ -138,13 +283,19 @@ impl Handler for SSRHandler

{ } } +/// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub struct ServerFnHandler { server_context: DioxusServerContext, function: Arc, } impl ServerFnHandler { - pub fn new(server_context: DioxusServerContext, function: Arc) -> Self { + /// Create a new server function handler with the given server context and server function. + pub fn new( + server_context: impl Into, + function: Arc, + ) -> Self { + let server_context = server_context.into(); Self { server_context, function, @@ -234,6 +385,7 @@ fn handle_error(error: impl Error + Send + Sync, res: &mut Response) { *res = resp_err; } +/// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change. #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] #[derive(Default)] pub struct HotReloadHandler; @@ -251,6 +403,7 @@ impl HotReloadHandler { } } +/// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change. #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] #[derive(Default)] pub struct HotReloadHandler { From b8ca41bd92019b293d902195759e712a121b91cd Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 12:15:59 -0500 Subject: [PATCH 335/516] document warp adapter --- packages/server/src/adapters/axum_adapter.rs | 30 ++-- packages/server/src/adapters/salvo_adapter.rs | 10 +- packages/server/src/adapters/warp_adapter.rs | 131 +++++++++++++++++- 3 files changed, 145 insertions(+), 26 deletions(-) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 34e6a5e46..c9bf9ba77 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -2,9 +2,9 @@ //! //! # Example //! ```rust -//! # #![allow(non_snake_case)] -//! # use dioxus::prelude::*; -//! # use dioxus_server::prelude::*; +//! #![allow(non_snake_case)] +//! use dioxus::prelude::*; +//! use dioxus_server::prelude::*; //! //! fn main() { //! #[cfg(feature = "web")] @@ -82,8 +82,8 @@ pub trait DioxusRouterExt { /// /// # Example /// ```rust - /// # use dioxus::prelude::*; - /// # use dioxus_server::prelude::*; + /// use dioxus::prelude::*; + /// use dioxus_server::prelude::*; /// /// #[tokio::main] /// async fn main() { @@ -117,8 +117,8 @@ pub trait DioxusRouterExt { /// /// # Example /// ```rust - /// # use dioxus::prelude::*; - /// # use dioxus_server::prelude::*; + /// use dioxus::prelude::*; + /// use dioxus_server::prelude::*; /// /// #[tokio::main] /// async fn main() { @@ -138,11 +138,9 @@ pub trait DioxusRouterExt { /// Register the web RSX hot reloading endpoint. This will enable hot reloading for your application in debug mode when you call [`dioxus_hot_reload::hot_reload_init`]. /// /// # Example - /// /// # Example /// ```rust - /// # #![allow(non_snake_case)] - /// # use dioxus::prelude::*; - /// # use dioxus_server::prelude::*; + /// #![allow(non_snake_case)] + /// use dioxus_server::prelude::*; /// /// #[tokio::main] /// async fn main() { @@ -158,8 +156,6 @@ pub trait DioxusRouterExt { /// .await /// .unwrap(); /// } - /// - /// # fn app(cx: Scope) -> Element {todo!()} /// ``` fn connect_hot_reload(self) -> Self; @@ -168,9 +164,9 @@ pub trait DioxusRouterExt { /// /// # Example /// ```rust - /// # #![allow(non_snake_case)] - /// # use dioxus::prelude::*; - /// # use dioxus_server::prelude::*; + /// #![allow(non_snake_case)] + /// use dioxus::prelude::*; + /// use dioxus_server::prelude::*; /// /// #[tokio::main] /// async fn main() { @@ -186,7 +182,7 @@ pub trait DioxusRouterExt { /// .unwrap(); /// } /// - /// # fn app(cx: Scope) -> Element {todo!()} + /// fn app(cx: Scope) -> Element {todo!()} /// ``` fn serve_dioxus_application( self, diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 6bc8947a6..ffe72bef5 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -2,9 +2,9 @@ //! //! # Example //! ```rust -//! # #![allow(non_snake_case)] -//! # use dioxus::prelude::*; -//! # use dioxus_server::prelude::*; +//! #![allow(non_snake_case)] +//! use dioxus::prelude::*; +//! use dioxus_server::prelude::*; //! //! fn main() { //! #[cfg(feature = "web")] @@ -48,6 +48,7 @@ //! async fn get_server_data() -> Result { //! Ok("Hello from the server!".to_string()) //! } +//! ``` use std::{error::Error, sync::Arc}; @@ -71,6 +72,7 @@ use crate::{ pub trait DioxusRouterExt { /// Registers server functions with a custom handler function. This allows you to pass custom context to your server functions by generating a [`DioxusServerContext`] from the request. /// + /// # Example /// ```rust /// use salvo::prelude::*; /// use std::{net::TcpListener, sync::Arc}; @@ -172,7 +174,7 @@ pub trait DioxusRouterExt { /// .await; /// } /// - /// # fn app(cx: Scope) -> Element {todo!()} + /// fn app(cx: Scope) -> Element {todo!()} /// ``` fn serve_dioxus_application( self, diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 67c3c75e4..5041e294d 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -1,3 +1,52 @@ +//! Dioxus utilities for the [Warp](https://docs.rs/warp/latest/warp/index.html) server framework. +//! +//! # Example +//! ```rust +//! #![allow(non_snake_case)] +//! use dioxus::prelude::*; +//! use dioxus_server::prelude::*; +//! +//! fn main() { +//! #[cfg(feature = "web")] +//! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); +//! #[cfg(feature = "ssr")] +//! { +//! GetServerData::register().unwrap(); +//! tokio::runtime::Runtime::new() +//! .unwrap() +//! .block_on(async move { +//! let routes = serve_dioxus_application("", ServeConfigBuilder::new(app, ())); +//! warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; +//! }); +//! } +//! } +//! +//! fn app(cx: Scope) -> Element { +//! let text = use_state(cx, || "...".to_string()); +//! +//! cx.render(rsx! { +//! button { +//! onclick: move |_| { +//! to_owned![text]; +//! async move { +//! if let Ok(data) = get_server_data().await { +//! text.set(data); +//! } +//! } +//! }, +//! "Run a server function" +//! } +//! "Server said: {text}" +//! }) +//! } +//! +//! #[server(GetServerData)] +//! async fn get_server_data() -> Result { +//! Ok("Hello from the server!".to_string()) +//! } +//! +//! ``` + use std::{error::Error, sync::Arc}; use server_fn::{Payload, ServerFunctionRegistry}; @@ -10,11 +59,36 @@ use warp::{ }; use crate::{ - prelude::{DioxusServerContext, SSRState}, - serve::ServeConfig, + prelude::DioxusServerContext, + render::SSRState, + serve_config::ServeConfig, server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, }; +/// Registers server functions with a custom handler function. This allows you to pass custom context to your server functions by generating a [`DioxusServerContext`] from the request. +/// +/// # Example +/// ```rust +/// use warp::{body, header, hyper::HeaderMap, path, post, Filter}; +/// +/// #[tokio::main] +/// async fn main() { +/// let routes = register_server_fns_with_handler("", |full_route, func| { +/// path(full_route) +/// .and(post()) +/// .and(header::headers_cloned()) +/// .and(body::bytes()) +/// .and_then(move |headers: HeaderMap, body| { +/// let func = func.clone(); +/// async move { +/// // Add the headers to the server function context +/// server_fn_handler((headers.clone(),), func, headers, body).await +/// } +/// }) +/// }); +/// warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; +/// } +/// ``` pub fn register_server_fns_with_handler( server_fn_route: &'static str, mut handler: H, @@ -41,6 +115,18 @@ where filter.expect("No server functions found") } +/// Registers server functions with the default handler. This handler function will pass an empty [`DioxusServerContext`] to your server functions. +/// +/// # Example +/// ```rust +/// use dioxus_server::prelude::*; +/// +/// #[tokio::main] +/// async fn main() { +/// let routes = register_server_fns(""); +/// warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; +/// } +/// ``` pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { register_server_fns_with_handler(server_fn_route, |full_route, func| { path(full_route) @@ -56,6 +142,25 @@ pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl R }) } +/// Serves the Dioxus application. This will serve a complete server side rendered application. +/// This will serve static assets, server render the application, register server functions, and intigrate with hot reloading. +/// +/// # Example +/// ```rust +/// #![allow(non_snake_case)] +/// use dioxus::prelude::*; +/// use dioxus_server::prelude::*; +/// +/// #[tokio::main] +/// async fn main() { +/// let routes = serve_dioxus_application("", ServeConfigBuilder::new(app, ())); +/// warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; +/// } +/// +/// fn app(cx: Scope) -> Element { +/// todo!() +/// } +/// ``` pub fn serve_dioxus_application( server_fn_route: &'static str, cfg: impl Into>, @@ -90,12 +195,14 @@ struct RecieveFailed(String); impl warp::reject::Reject for RecieveFailed {} -async fn server_fn_handler( - server_context: DioxusServerContext, +/// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. +pub async fn server_fn_handler( + server_context: impl Into, function: Arc, headers: HeaderMap, body: Bytes, ) -> Result, warp::Rejection> { + let server_context = server_context.into(); // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); spawn_blocking({ @@ -161,7 +268,21 @@ fn report_err(e: E) -> Box { ) as Box } -pub fn connect_hot_reload() -> impl Filter { +/// Register the web RSX hot reloading endpoint. This will enable hot reloading for your application in debug mode when you call [`dioxus_hot_reload::hot_reload_init`]. +/// +/// # Example +/// ```rust +/// #![allow(non_snake_case)] +/// use dioxus_server::prelude::*; +/// +/// #[tokio::main] +/// async fn main() { +/// let routes = connect_hot_reload(); +/// warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; +/// } +/// ``` +pub fn connect_hot_reload() -> impl Filter + Clone +{ #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] { warp::path("_dioxus/hot_reload").and(warp::ws()).map(|| { From 10a1c46a49458db192410118394ee74e741c73e0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 12:45:01 -0500 Subject: [PATCH 336/516] make server crate example more compelling --- packages/server/src/lib.rs | 40 +++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 9654adebe..7ef232c23 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -6,6 +6,7 @@ //! - Instant RSX Hot reloading with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload). //! //! # Example +//! Full stack Dioxus in under 50 lines of code //! ```rust //! #![allow(non_snake_case)] //! use dioxus::prelude::*; @@ -13,50 +14,45 @@ //! //! fn main() { //! #[cfg(feature = "web")] -//! // Hydrate the application on the client //! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); //! #[cfg(feature = "ssr")] //! { -//! GetServerData::register().unwrap(); +//! GetMeaning::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { -//! let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); -//! axum::Server::bind(&addr) -//! .serve( -//! axum::Router::new() -//! // Server side render the application, serve static assets, and register server functions -//! .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) -//! .into_make_service(), -//! ) -//! .await -//! .unwrap(); +//! warp::serve(serve_dioxus_application( +//! "", +//! ServeConfigBuilder::new(app, ()), +//! )) +//! .run(([127, 0, 0, 1], 8080)) +//! .await; //! }); -//! } +//! } //! } //! //! fn app(cx: Scope) -> Element { -//! let text = use_state(cx, || "...".to_string()); -//! +//! let meaning = use_state(cx, || None); //! cx.render(rsx! { //! button { //! onclick: move |_| { -//! to_owned![text]; +//! to_owned![meaning]; //! async move { -//! if let Ok(data) = get_server_data().await { -//! text.set(data.clone()); +//! if let Ok(data) = get_meaning("life the universe and everything".into()).await { +//! meaning.set(data); //! } //! } //! }, //! "Run a server function" //! } -//! "Server said: {text}" +//! "Server said: {meaning:?}" //! }) //! } //! -//! #[server(GetServerData)] -//! async fn get_server_data() -> Result { -//! Ok("Hello from the server!".to_string()) +//! // This code will only run on the server +//! #[server(GetMeaning)] +//! async fn get_meaning(of: String) -> Result, ServerFnError> { +//! Ok(of.contains("life").then(|| 42)) //! } //! ``` From 830dd6fa9737f6ead349093f3a492828ab341a4f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 13:09:25 -0500 Subject: [PATCH 337/516] use git version of server functions --- packages/server/Cargo.toml | 2 +- packages/server/server-macro/Cargo.toml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 563912754..fddeccbde 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] # server functions -server_fn = { path = "D:/Users/Desktop/github/leptos/server_fn", features = ["stable"] } +server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] } server_macro = { path = "server-macro" } # warp diff --git a/packages/server/server-macro/Cargo.toml b/packages/server/server-macro/Cargo.toml index e5fb6523e..d03c5274c 100644 --- a/packages/server/server-macro/Cargo.toml +++ b/packages/server/server-macro/Cargo.toml @@ -7,8 +7,7 @@ edition = "2021" [dependencies] quote = "1.0.26" -# server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "1e037ecb60965c7c55fd781fdc8de7863ffd102b", features = ["stable"] } -server_fn_macro = { path = "D:/Users/Desktop/github/leptos/server_fn_macro", features = ["stable"] } +server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] } syn = { version = "1", features = ["full"] } [lib] From f1c31d6cc368607c1310452b2554b4e06c936e2d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 13:09:34 -0500 Subject: [PATCH 338/516] mark examples as don't publish --- packages/server/examples/axum-hello-world/Cargo.toml | 1 + packages/server/examples/axum-router/Cargo.toml | 1 + packages/server/examples/salvo-hello-world/Cargo.toml | 1 + packages/server/examples/warp-hello-world/Cargo.toml | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/server/examples/axum-hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml index ed466e6e1..50d5e05b3 100644 --- a/packages/server/examples/axum-hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -2,6 +2,7 @@ name = "axum-hello-world" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/packages/server/examples/axum-router/Cargo.toml b/packages/server/examples/axum-router/Cargo.toml index 297aa6726..699779763 100644 --- a/packages/server/examples/axum-router/Cargo.toml +++ b/packages/server/examples/axum-router/Cargo.toml @@ -2,6 +2,7 @@ name = "axum-router" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/packages/server/examples/salvo-hello-world/Cargo.toml b/packages/server/examples/salvo-hello-world/Cargo.toml index 4633eaafc..1ddf52915 100644 --- a/packages/server/examples/salvo-hello-world/Cargo.toml +++ b/packages/server/examples/salvo-hello-world/Cargo.toml @@ -2,6 +2,7 @@ name = "salvo-hello-world" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/packages/server/examples/warp-hello-world/Cargo.toml b/packages/server/examples/warp-hello-world/Cargo.toml index 86ac2da5b..cbd8606dd 100644 --- a/packages/server/examples/warp-hello-world/Cargo.toml +++ b/packages/server/examples/warp-hello-world/Cargo.toml @@ -2,6 +2,7 @@ name = "warp-hello-world" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 7ae45272d1e7ee642501d574ecb32f9a7b6d9345 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 13:20:38 -0500 Subject: [PATCH 339/516] fix some doc links in dioxus-server --- packages/server/server-macro/src/lib.rs | 6 +++--- packages/server/src/adapters/axum_adapter.rs | 4 +++- packages/server/src/adapters/mod.rs | 4 ++-- packages/server/src/lib.rs | 6 ++++-- packages/server/src/server_fn.rs | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/server/server-macro/src/lib.rs b/packages/server/server-macro/src/lib.rs index f6b4f92a2..e34a440d4 100644 --- a/packages/server/server-macro/src/lib.rs +++ b/packages/server/server-macro/src/lib.rs @@ -19,7 +19,7 @@ use server_fn_macro::*; /// work without WebAssembly, the encoding must be `"Url"`. /// /// The server function itself can take any number of arguments, each of which should be serializable -/// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](dioxus_server::prelude::DioxusServerContext), +/// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](https::/docs.rs/dioxus_server/latest/dixous_server/prelude/struct.DioxusServerContext.html), /// which will be injected *on the server side.* This can be used to inject the raw HTTP request or other /// server-side context into the server function. /// @@ -50,8 +50,8 @@ use server_fn_macro::*; /// They are serialized as an `application/x-www-form-urlencoded` /// form data using [`serde_urlencoded`](https://docs.rs/serde_urlencoded/latest/serde_urlencoded/) or as `application/cbor` /// using [`cbor`](https://docs.rs/cbor/latest/cbor/). -/// - **The [DioxusServerContext](dioxus_server::prelude::DioxusServerContext) comes from the server.** Optionally, the first argument of a server function -/// can be a [DioxusServerContext](dioxus_server::prelude::DioxusServerContext). This scope can be used to inject dependencies like the HTTP request +/// - **The [DioxusServerContext](https::/docs.rs/dioxus_server/latest/dixous_server/prelude/struct.DioxusServerContext.html) comes from the server.** Optionally, the first argument of a server function +/// can be a [DioxusServerContext](https::/docs.rs/dioxus_server/latest/dixous_server/prelude/struct.DioxusServerContext.html). This scope can be used to inject dependencies like the HTTP request /// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client. #[proc_macro_attribute] pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index c9bf9ba77..f4bdd8a96 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -182,7 +182,9 @@ pub trait DioxusRouterExt { /// .unwrap(); /// } /// - /// fn app(cx: Scope) -> Element {todo!()} + /// fn app(cx: Scope) -> Element { + /// todo!() + /// } /// ``` fn serve_dioxus_application( self, diff --git a/packages/server/src/adapters/mod.rs b/packages/server/src/adapters/mod.rs index 148062db0..5809132b8 100644 --- a/packages/server/src/adapters/mod.rs +++ b/packages/server/src/adapters/mod.rs @@ -6,8 +6,8 @@ //! Each framework has utilies for some or all of the following: //! - Server functions //! - A generic way to register server functions -//! - A way to register server functions with a custom handler that allows users to pass in a custom [`DioxusServerContext`] based on the state of the server framework. -//! - A way to register static WASM files that is accepts [`ServeConfig`] +//! - A way to register server functions with a custom handler that allows users to pass in a custom [`crate::server_context::DioxusServerContext`] based on the state of the server framework. +//! - A way to register static WASM files that is accepts [`crate::serve_config::ServeConfig`] //! - A hot reloading web socket that intigrates with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload) #[cfg(feature = "axum")] diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 7ef232c23..4738ef00f 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -1,8 +1,8 @@ //! Fullstack utilities for the [`dioxus`](https://dioxuslabs.com) framework. //! //! # Features -//! - Intigrations with the [axum](crate::adapters::axum_adapter), [salvo](crate::adapters::salvo_adapters), and [warp](crate::adapters::warp_adapters) server frameworks with utilities for serving and rendering Dioxus applications. -//! - Server functions that allow you to call code on the server from the client as if it were a normal function. +//! - Intigrations with the [axum](crate::adapters::axum_adapter), [salvo](crate::adapters::salvo_adapter), and [warp](crate::adapters::warp_adapter) server frameworks with utilities for serving and rendering Dioxus applications. +//! - [Server functions](crate::prelude::server) that allow you to call code on the server from the client as if it were a normal function. //! - Instant RSX Hot reloading with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload). //! //! # Example @@ -60,6 +60,8 @@ #[allow(unused)] use dioxus_core::prelude::*; +pub use adapters::*; + mod adapters; #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] mod hot_reload; diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs index 6f985a39a..643f25c9f 100644 --- a/packages/server/src/server_fn.rs +++ b/packages/server/src/server_fn.rs @@ -82,7 +82,7 @@ pub enum ServerRegistrationFnError { /// Server functions are created using the `server` macro. /// /// The function should be registered by calling `ServerFn::register()`. The set of server functions -/// can be queried on the server for routing purposes by calling [ServerFunctionRegistry::get]. +/// can be queried on the server for routing purposes by calling [server_fn::ServerFunctionRegistry::get]. /// /// Technically, the trait is implemented on a type that describes the server function's arguments, not the function itself. pub trait ServerFn: server_fn::ServerFn { From 0e445c9f5958eb5b2e64cf9fbe22e3b56978b3df Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 13:34:34 -0500 Subject: [PATCH 340/516] add desktop server functions example --- Cargo.toml | 3 +- .../server/examples/axum-desktop/.gitignore | 2 + .../server/examples/axum-desktop/Cargo.toml | 31 ++++++++++++++ .../examples/axum-desktop/src/client.rs | 13 ++++++ .../server/examples/axum-desktop/src/lib.rs | 40 +++++++++++++++++++ .../examples/axum-desktop/src/server.rs | 23 +++++++++++ .../examples/axum-hello-world/Cargo.toml | 3 +- 7 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 packages/server/examples/axum-desktop/.gitignore create mode 100644 packages/server/examples/axum-desktop/Cargo.toml create mode 100644 packages/server/examples/axum-desktop/src/client.rs create mode 100644 packages/server/examples/axum-desktop/src/lib.rs create mode 100644 packages/server/examples/axum-desktop/src/server.rs diff --git a/Cargo.toml b/Cargo.toml index 720b9e274..a5beb4aeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,9 +25,10 @@ members = [ "packages/server", "packages/server/server-macro", "packages/server/examples/axum-hello-world", + "packages/server/examples/axum-router", + "packages/server/examples/axum-desktop", "packages/server/examples/salvo-hello-world", "packages/server/examples/warp-hello-world", - "packages/server/examples/axum-router", "docs/guide", ] diff --git a/packages/server/examples/axum-desktop/.gitignore b/packages/server/examples/axum-desktop/.gitignore new file mode 100644 index 000000000..6047329c6 --- /dev/null +++ b/packages/server/examples/axum-desktop/.gitignore @@ -0,0 +1,2 @@ +dist +target \ No newline at end of file diff --git a/packages/server/examples/axum-desktop/Cargo.toml b/packages/server/examples/axum-desktop/Cargo.toml new file mode 100644 index 000000000..817202bd1 --- /dev/null +++ b/packages/server/examples/axum-desktop/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "axum-desktop" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] + +[dependencies] +dioxus-desktop = { path = "../../../desktop", optional = true } +dioxus = { path = "../../../dioxus" } +dioxus-router = { path = "../../../router" } +dioxus-server = { path = "../../" } +axum = { version = "0.6.12", optional = true } +tokio = { version = "1.27.0", features = ["full"], optional = true } +serde = "1.0.159" + +[features] +default = [] +ssr = ["axum", "tokio", "dioxus-server/axum"] +desktop = ["dioxus-desktop"] + +[[bin]] +name = "client" +path = "src/client.rs" +required-features = ["desktop"] + +[[bin]] +name = "server" +path = "src/server.rs" +required-features = ["ssr"] diff --git a/packages/server/examples/axum-desktop/src/client.rs b/packages/server/examples/axum-desktop/src/client.rs new file mode 100644 index 000000000..588cf2003 --- /dev/null +++ b/packages/server/examples/axum-desktop/src/client.rs @@ -0,0 +1,13 @@ +// Run with: +// ```bash +// cargo run --bin client --features="desktop" +// ``` + +use axum_desktop::*; +use dioxus_server::prelude::server_fn::set_server_url; + +fn main() { + // Set the url of the server where server functions are hosted. + set_server_url("http://localhost:8080"); + dioxus_desktop::launch(app) +} diff --git a/packages/server/examples/axum-desktop/src/lib.rs b/packages/server/examples/axum-desktop/src/lib.rs new file mode 100644 index 000000000..3bae1dcde --- /dev/null +++ b/packages/server/examples/axum-desktop/src/lib.rs @@ -0,0 +1,40 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_server::prelude::*; + +pub fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + let text = use_state(cx, || "...".to_string()); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![text]; + async move { + if let Ok(data) = get_server_data().await { + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await.unwrap(); + } + } + }, + "Run a server function" + } + "Server said: {text}" + }) +} + +#[server(PostServerData)] +async fn post_server_data(data: String) -> Result<(), ServerFnError> { + println!("Server received: {}", data); + + Ok(()) +} + +#[server(GetServerData)] +async fn get_server_data() -> Result { + Ok("Hello from the server!".to_string()) +} diff --git a/packages/server/examples/axum-desktop/src/server.rs b/packages/server/examples/axum-desktop/src/server.rs new file mode 100644 index 000000000..ff607dbcd --- /dev/null +++ b/packages/server/examples/axum-desktop/src/server.rs @@ -0,0 +1,23 @@ +// Run with: +// ```bash +// cargo run --bin server --features="ssr" +// ``` + +use axum_desktop::*; +use dioxus_server::prelude::*; + +#[tokio::main] +async fn main() { + PostServerData::register().unwrap(); + GetServerData::register().unwrap(); + + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + .register_server_fns("") + .into_make_service(), + ) + .await + .unwrap(); +} diff --git a/packages/server/examples/axum-hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml index 50d5e05b3..fd1f051d1 100644 --- a/packages/server/examples/axum-hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -9,7 +9,6 @@ publish = false [dependencies] dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } dioxus = { path = "../../../dioxus" } -dioxus-router = { path = "../../../router" } dioxus-server = { path = "../../" } axum = { version = "0.6.12", optional = true } tokio = { version = "1.27.0", features = ["full"], optional = true } @@ -18,4 +17,4 @@ serde = "1.0.159" [features] default = ["web"] ssr = ["axum", "tokio", "dioxus-server/axum"] -web = ["dioxus-web", "dioxus-router/web"] +web = ["dioxus-web"] From 53c85851076462cd002a6c85135cca5f7bdbe1b0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 13:58:54 -0500 Subject: [PATCH 341/516] Add dixous-server README --- packages/server/Cargo.toml | 6 ++ packages/server/README.md | 103 ++++++++++++++++++++++++ packages/server/server-macro/src/lib.rs | 6 +- packages/server/src/lib.rs | 65 +-------------- 4 files changed, 116 insertions(+), 64 deletions(-) create mode 100644 packages/server/README.md diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index fddeccbde..8709cb57d 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -2,6 +2,12 @@ name = "dioxus-server" version = "0.1.0" edition = "2021" +description = "Fullstack Dioxus Utilities" +license = "MIT/Apache-2.0" +repository = "https://github.com/DioxusLabs/dioxus/" +homepage = "https://dioxuslabs.com" +documentation = "https://dioxuslabs.com" +keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/packages/server/README.md b/packages/server/README.md new file mode 100644 index 000000000..92f878684 --- /dev/null +++ b/packages/server/README.md @@ -0,0 +1,103 @@ +# Dioxus Server + +[![Crates.io][crates-badge]][crates-url] +[![MIT licensed][mit-badge]][mit-url] +[![Build Status][actions-badge]][actions-url] +[![Discord chat][discord-badge]][discord-url] + +[crates-badge]: https://img.shields.io/crates/v/dioxus-server.svg +[crates-url]: https://crates.io/crates/dioxus-server +[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg +[mit-url]: https://github.com/dioxuslabs/dioxus/blob/master/LICENSE +[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg +[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster +[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square +[discord-url]: https://discord.gg/XgGxMSkvUM + +[Website](https://dioxuslabs.com) | +[Guides](https://dioxuslabs.com/docs/0.3/guide/en/) | +[API Docs](https://docs.rs/dioxus-server/latest/dioxus_sever) | +[Chat](https://discord.gg/XgGxMSkvUM) + +Fullstack utilities for the [`Dioxus`](https://dioxuslabs.com) framework. + +# Features + +- Intigrations with the [Axum](https::/docs.rs/dioxus-server/latest/dixous_server/axum_adapter/index.html), [Salvo](https::/docs.rs/dioxus-server/latest/dixous_server/salvo_adapter/index.html), and [Warp](https::/docs.rs/dioxus-server/latest/dixous_server/warp_adapter/index.html) server frameworks with utilities for serving and rendering Dioxus applications. +- [Server functions](https::/docs.rs/dioxus-server/latest/dixous_server/prelude/attr.server.html) allow you to call code on the server from the client as if it were a normal function. +- Instant RSX Hot reloading with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload). + +# Example + +Full stack Dioxus in under 50 lines of code + +```rust +#![allow(non_snake_case)] +use dioxus::prelude::*; +use dioxus_server::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + GetMeaning::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + warp::serve( + // Automatically handles server side rendering, hot reloading intigration, and hosting server functions + serve_dioxus_application( + "", + ServeConfigBuilder::new(app, ()), + ) + ) + .run(([127, 0, 0, 1], 8080)) + .await; + }); + } +} + +fn app(cx: Scope) -> Element { + let meaning = use_state(cx, || None); + cx.render(rsx! { + button { + onclick: move |_| { + to_owned![meaning]; + async move { + if let Ok(data) = get_meaning("life the universe and everything".into()).await { + meaning.set(data); + } + } + }, + "Run a server function" + } + "Server said: {meaning:?}" + }) +} + +// This code will only run on the server +#[server(GetMeaning)] +async fn get_meaning(of: String) -> Result, ServerFnError> { + Ok(of.contains("life").then(|| 42)) +} +``` + +## Getting Started + +To get started with full stack Dioxus, check out our [getting started guide](https://dioxuslabs.com/docs/nightly/guide/en/getting_started/ssr.html), or the [full stack examples](https://github.com/DioxusLabs/dioxus/tree/master/packages/server/examples). + +## Contributing + +- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues). +- Join the discord and ask questions! + +## License + +This project is licensed under the [MIT license]. + +[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in Dioxus by you shall be licensed as MIT without any additional +terms or conditions. diff --git a/packages/server/server-macro/src/lib.rs b/packages/server/server-macro/src/lib.rs index e34a440d4..14fc0ac77 100644 --- a/packages/server/server-macro/src/lib.rs +++ b/packages/server/server-macro/src/lib.rs @@ -19,7 +19,7 @@ use server_fn_macro::*; /// work without WebAssembly, the encoding must be `"Url"`. /// /// The server function itself can take any number of arguments, each of which should be serializable -/// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](https::/docs.rs/dioxus_server/latest/dixous_server/prelude/struct.DioxusServerContext.html), +/// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](https::/docs.rs/dioxus-server/latest/dixous_server/prelude/struct.DioxusServerContext.html), /// which will be injected *on the server side.* This can be used to inject the raw HTTP request or other /// server-side context into the server function. /// @@ -50,8 +50,8 @@ use server_fn_macro::*; /// They are serialized as an `application/x-www-form-urlencoded` /// form data using [`serde_urlencoded`](https://docs.rs/serde_urlencoded/latest/serde_urlencoded/) or as `application/cbor` /// using [`cbor`](https://docs.rs/cbor/latest/cbor/). -/// - **The [DioxusServerContext](https::/docs.rs/dioxus_server/latest/dixous_server/prelude/struct.DioxusServerContext.html) comes from the server.** Optionally, the first argument of a server function -/// can be a [DioxusServerContext](https::/docs.rs/dioxus_server/latest/dixous_server/prelude/struct.DioxusServerContext.html). This scope can be used to inject dependencies like the HTTP request +/// - **The [DioxusServerContext](https::/docs.rs/dioxus-server/latest/dixous_server/prelude/struct.DioxusServerContext.html) comes from the server.** Optionally, the first argument of a server function +/// can be a [DioxusServerContext](https::/docs.rs/dioxus-server/latest/dixous_server/prelude/struct.DioxusServerContext.html). This scope can be used to inject dependencies like the HTTP request /// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client. #[proc_macro_attribute] pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 4738ef00f..c61b26429 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -1,64 +1,7 @@ -//! Fullstack utilities for the [`dioxus`](https://dioxuslabs.com) framework. -//! -//! # Features -//! - Intigrations with the [axum](crate::adapters::axum_adapter), [salvo](crate::adapters::salvo_adapter), and [warp](crate::adapters::warp_adapter) server frameworks with utilities for serving and rendering Dioxus applications. -//! - [Server functions](crate::prelude::server) that allow you to call code on the server from the client as if it were a normal function. -//! - Instant RSX Hot reloading with [`dioxus-hot-reload`](https://crates.io/crates/dioxus-hot-reload). -//! -//! # Example -//! Full stack Dioxus in under 50 lines of code -//! ```rust -//! #![allow(non_snake_case)] -//! use dioxus::prelude::*; -//! use dioxus_server::prelude::*; -//! -//! fn main() { -//! #[cfg(feature = "web")] -//! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); -//! #[cfg(feature = "ssr")] -//! { -//! GetMeaning::register().unwrap(); -//! tokio::runtime::Runtime::new() -//! .unwrap() -//! .block_on(async move { -//! warp::serve(serve_dioxus_application( -//! "", -//! ServeConfigBuilder::new(app, ()), -//! )) -//! .run(([127, 0, 0, 1], 8080)) -//! .await; -//! }); -//! } -//! } -//! -//! fn app(cx: Scope) -> Element { -//! let meaning = use_state(cx, || None); -//! cx.render(rsx! { -//! button { -//! onclick: move |_| { -//! to_owned![meaning]; -//! async move { -//! if let Ok(data) = get_meaning("life the universe and everything".into()).await { -//! meaning.set(data); -//! } -//! } -//! }, -//! "Run a server function" -//! } -//! "Server said: {meaning:?}" -//! }) -//! } -//! -//! // This code will only run on the server -//! #[server(GetMeaning)] -//! async fn get_meaning(of: String) -> Result, ServerFnError> { -//! Ok(of.contains("life").then(|| 42)) -//! } -//! ``` - -#![warn(missing_docs)] -#[allow(unused)] -use dioxus_core::prelude::*; +#![doc = include_str!("../README.md")] +#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] +#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] +#![deny(missing_docs)] pub use adapters::*; From 4c22d5809e92014d19628db467ad7eb41345d3bd Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 16:06:23 -0500 Subject: [PATCH 342/516] add fullstack section to the getting started guide --- docs/guide/Cargo.toml | 3 +- docs/guide/examples/server.rs | 132 ++++++++++++++++++ docs/guide/src/en/SUMMARY.md | 1 + .../guide/src/en/getting_started/fullstack.md | 115 +++++++++++++++ .../src/en/getting_started/hot_reload.md | 15 +- docs/guide/src/en/getting_started/ssr.md | 21 +-- docs/guide/src/en/getting_started/web.md | 6 +- 7 files changed, 273 insertions(+), 20 deletions(-) create mode 100644 docs/guide/examples/server.rs create mode 100644 docs/guide/src/en/getting_started/fullstack.md diff --git a/docs/guide/Cargo.toml b/docs/guide/Cargo.toml index 227bdfa35..00a5c7543 100644 --- a/docs/guide/Cargo.toml +++ b/docs/guide/Cargo.toml @@ -16,6 +16,7 @@ dioxus-native-core-macro = { path = "../../packages/native-core-macro" } dioxus-router = { path = "../../packages/router" } dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] } dioxus-tui = { path = "../../packages/dioxus-tui" } +dioxus-server = { path = "../../packages/server" } fermi = { path = "../../packages/fermi" } shipyard = "0.6.2" @@ -23,5 +24,5 @@ shipyard = "0.6.2" # dioxus = { path = "../../packages/dioxus", features = ["desktop", "web", "ssr", "router", "fermi", "tui"] } serde = { version = "1.0.138", features=["derive"] } reqwest = { version = "0.11.11", features = ["json"] } -tokio = { version = "1.19.2" , features=[]} +tokio = { version = "1.19.2", features = ["full"] } axum = { version = "0.6.1", features = ["ws"] } diff --git a/docs/guide/examples/server.rs b/docs/guide/examples/server.rs new file mode 100644 index 000000000..80e74a919 --- /dev/null +++ b/docs/guide/examples/server.rs @@ -0,0 +1,132 @@ +mod basic { + // ANCHOR: basic + #![allow(non_snake_case)] + use dioxus::prelude::*; + use dioxus_server::prelude::*; + + #[tokio::main] + async fn main() { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .into_make_service(), + ) + .await + .unwrap(); + } + + fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + }) + } + // ANCHOR_END: basic +} + +mod hydration { + // ANCHOR: hydration + #![allow(non_snake_case)] + use dioxus::prelude::*; + use dioxus_server::prelude::*; + + fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .into_make_service(), + ) + .await + .unwrap(); + }); + } + } + + fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + }) + } + // ANCHOR_END: hydration +} + +mod server_function { + // ANCHOR: server_function + #![allow(non_snake_case)] + use dioxus::prelude::*; + use dioxus_server::prelude::*; + + fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + // Register the server function before starting the server + DoubleServer::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + // Serve Dioxus application automatically recognizes server functions and adds them to the API + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .into_make_service(), + ) + .await + .unwrap(); + }); + } + } + + fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![count]; + async move { + // Call the server function just like a local async function + if let Ok(new_count) = double_server(*count.current()).await { + count.set(new_count); + } + } + }, + "Double" + } + }) + } + + #[server(DoubleServer)] + async fn double_server(number: u32) -> Result { + // Perform some expensive computation or access a database on the server + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + let result = number * 2; + println!("server calculated {result}"); + Ok(result) + } + // ANCHOR_END: server_function +} diff --git a/docs/guide/src/en/SUMMARY.md b/docs/guide/src/en/SUMMARY.md index f9f31ed52..b61a36117 100644 --- a/docs/guide/src/en/SUMMARY.md +++ b/docs/guide/src/en/SUMMARY.md @@ -6,6 +6,7 @@ - [Desktop](getting_started/desktop.md) - [Web](getting_started/web.md) - [Server-Side Rendering](getting_started/ssr.md) + - [Fullstack](getting_started/fullstack.md) - [Liveview](getting_started/liveview.md) - [Terminal UI](getting_started/tui.md) - [Mobile](getting_started/mobile.md) diff --git a/docs/guide/src/en/getting_started/fullstack.md b/docs/guide/src/en/getting_started/fullstack.md new file mode 100644 index 000000000..b132de319 --- /dev/null +++ b/docs/guide/src/en/getting_started/fullstack.md @@ -0,0 +1,115 @@ +> This guide assumes you read the [Web](web.md) guide and installed the [Dioxus-cli](https://github.com/DioxusLabs/cli) + +# Fullstack development + +We can combine the `dioxus-web` renderer with the `dioxus-ssr` renderer to create a full-stack Dioxus application. By combining server-side rendering with client-side hydration we can create an application that is initially rendered on the server and then hydrates the application on the client. Server-side rendering results in a fast first paint and make our page SEO-friendly. Client-side hydration makes our page responsive once the application has fully loaded. + +To help make full-stack development easier, Dioxus provides a `dioxus-server` crate that integrates with popular web frameworks with utilities for full-stack development. + +## Setup + +For this guide, we're going to show how to use Dioxus with [Axum](https://docs.rs/axum/latest/axum/), but `dioxus-server` also integrates with the [Warp](https://docs.rs/warp/latest/warp/) and [Salvo](https://docs.rs/salvo/latest/salvo/) web frameworks. + +Make sure you have Rust and Cargo installed, and then create a new project: + +```shell +cargo new --bin demo +cd demo +``` + +Add `dioxus` and `dioxus-server` as dependencies: + +```shell +cargo add dioxus +cargo add dioxus-server --features axum, ssr +``` + +Next, add all the Axum dependencies. This will be different if you're using a different Web Framework + +```shell +cargo add tokio --features full +cargo add axum +``` + +Your dependencies should look roughly like this: + +```toml +[dependencies] +axum = "*" +dioxus = { version = "*" } +dioxus-server = { version = "*", features = ["axum", "ssr"] } +tokio = { version = "*", features = ["full"] } +``` + +Now, set up your Axum app to serve the Dioxus app. + +```rust +{{#include ../../../examples/server.rs:basic}} +``` + +Now, run your app with `cargo run` and open `http://localhost:8080` in your browser. You should see a server-side rendered page with a counter. + +## Hydration + +Right now, the page is static. We can't interact with the buttons. To fix this, we can hydrate the page with `dioxus-web`. + +First, modify your `Cargo.toml` to include two features, one for the server called `ssr`, and one for the client called `web`. + +```toml +[dependencies] +# Common dependancies +dioxus = { version = "*" } +dioxus-server = { version = "*" } + +# Web dependancies +dioxus-web = { version = "*", features=["hydrate"], optional = true } + +# Server dependancies +axum = { version = "0.6.12", optional = true } +tokio = { version = "1.27.0", features = ["full"], optional = true } + +[features] +default = [] +ssr = ["axum", "tokio", "dioxus-server/axum"] +web = ["dioxus-web"] +``` + +Next, we need to modify our `main.rs` to use either hydrate on the client or render on the server depending on the active features. + +```rust +{{#include ../../../examples/server.rs:hydration}} +``` + +Now, build your client-side bundle with `dioxus build --features web` and run your server with `cargo run --features ssr`. You should see the same page as before, but now you can interact with the buttons! + +## Communicating with the server + +`dixous-server` provides server functions that allow you to call an automatically generated API on the server from the client as if it were a local function. + +To make a server function, simply add the `#[server(YourUniqueType)]` attribute to a function. The function must: + +- Be an async function +- Have arguments and a return type that both implement serialize and deserialize (with [serde](https://serde.rs/)). +- Return a `Result` with an error type of ServerFnError + +You must call `register` on the type you passed into the server macro in your main function before starting your server to tell Dioxus about the server function. + +Let's add a server function to our app that allows us to multiply the count by a number on the server. + +First, add serde as a dependency: + +```shell +cargo add serde +``` + +Next, add the server function to your `main.rs`: + +```rust +{{#include ../../../examples/server.rs:server_function}} +``` + +Now, build your client-side bundle with `dioxus build --features web` and run your server with `cargo run --features ssr`. You should see a new button that multiplies the count by 2. + +## Conclusion + +That's it! You've created a full-stack Dioxus app. You can find more examples of full-stack apps and information about how to integrate with other frameworks and desktop renderers in the [dioxus-server examples directory](https://github.com/DioxusLabs/dioxus/tree/master/packages/server/examples). diff --git a/docs/guide/src/en/getting_started/hot_reload.md b/docs/guide/src/en/getting_started/hot_reload.md index 599ce57da..2ebf5e4aa 100644 --- a/docs/guide/src/en/getting_started/hot_reload.md +++ b/docs/guide/src/en/getting_started/hot_reload.md @@ -5,28 +5,35 @@ 3. Currently the cli only implements hot reloading for the web renderer. For TUI, desktop, and LiveView you can use the hot reload macro instead. # Web + For the web renderer, you can use the dioxus cli to serve your application with hot reloading enabled. ## Setup + Install [dioxus-cli](https://github.com/DioxusLabs/cli). Hot reloading is automatically enabled when using the web renderer on debug builds. ## Usage + 1. Run: -```bash + +```bash dioxus serve --hot-reload ``` + 2. Change some code within a rsx or render macro 3. Open your localhost in a browser 4. Save and watch the style change without recompiling -# Desktop/Liveview/TUI +# Desktop/Liveview/TUI/Server + For desktop, LiveView, and tui, you can place the hot reload macro at the top of your main function to enable hot reloading. Hot reloading is automatically enabled on debug builds. For more information about hot reloading on native platforms and configuration options see the [dioxus-hot-reload](https://crates.io/crates/dioxus-hot-reload) crate. ## Setup + Add the following to your main function: ```rust @@ -37,13 +44,17 @@ fn main() { ``` ## Usage + 1. Run: + ```bash cargo run ``` + 2. Change some code within a rsx or render macro 3. Save and watch the style change without recompiling # Limitations + 1. The interpreter can only use expressions that existed on the last full recompile. If you introduce a new variable or expression to the rsx call, it will require a full recompile to capture the expression. 2. Components, Iterators, and some attributes can contain arbitrary rust code and will trigger a full recompile when changed. diff --git a/docs/guide/src/en/getting_started/ssr.md b/docs/guide/src/en/getting_started/ssr.md index 9654003cf..f78623003 100644 --- a/docs/guide/src/en/getting_started/ssr.md +++ b/docs/guide/src/en/getting_started/ssr.md @@ -1,17 +1,6 @@ # Server-Side Rendering -The Dioxus VirtualDom can be rendered server-side. - -[Example: Dioxus DocSite](https://github.com/dioxusLabs/docsite) - -## Multithreaded Support - -The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc. - -To solve this, you'll want to spawn a VirtualDom on its own thread and communicate with it via channels. - -When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String – but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a pool of VirtualDoms. - +For lower-level control over the rendering process, you can use the `dioxus-ssr` crate directly. This can be useful when integrating with a web framework that `dioxus-server` does not support, or pre-rendering pages. ## Setup @@ -21,7 +10,7 @@ Make sure you have Rust and Cargo installed, and then create a new project: ```shell cargo new --bin demo -cd app +cd demo ``` Add Dioxus and the ssr renderer as dependencies: @@ -99,6 +88,8 @@ async fn app_endpoint() -> Html { } ``` -And that's it! +## Multithreaded Support -> You might notice that you cannot hold the VirtualDom across an await point. Dioxus is currently not ThreadSafe, so it _must_ remain on the thread it started. We are working on loosening this requirement. \ No newline at end of file +The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. +When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String – but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to spawn a VirtualDom on its own thread and communicate with it via channels or create a pool of VirtualDoms. +You might notice that you cannot hold the VirtualDom across an await point. Because Dioxus is currently not ThreadSafe, it _must_ remain on the thread it started. We are working on loosening this requirement. diff --git a/docs/guide/src/en/getting_started/web.md b/docs/guide/src/en/getting_started/web.md index 76e3b9541..e8a1f3da2 100644 --- a/docs/guide/src/en/getting_started/web.md +++ b/docs/guide/src/en/getting_started/web.md @@ -5,6 +5,7 @@ Build single-page applications that run in the browser with Dioxus. To run on th A build of Dioxus for the web will be roughly equivalent to the size of a React build (70kb vs 65kb) but it will load significantly faster because [WebAssembly can be compiled as it is streamed](https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/). Examples: + - [TodoMVC](https://github.com/DioxusLabs/example-projects/tree/master/todomvc) - [ECommerce](https://github.com/DioxusLabs/example-projects/tree/master/ecommerce-site) @@ -17,7 +18,7 @@ Examples: The Web is the best-supported target platform for Dioxus. - Because your app will be compiled to WASM you have access to browser APIs through [wasm-bingen](https://rustwasm.github.io/docs/wasm-bindgen/introduction.html). -- Dioxus provides hydration to resume apps that are rendered on the server. See the [hydration example](https://github.com/DioxusLabs/dioxus/blob/master/packages/web/examples/hydrate.rs) for more details. +- Dioxus provides hydration to resume apps that are rendered on the server. See the [fullstack](fullstack.md) getting started guide for more information. ## Tooling @@ -28,6 +29,7 @@ cargo install dioxus-cli ``` Make sure the `wasm32-unknown-unknown` target for rust is installed: + ```shell rustup target add wasm32-unknown-unknown ``` @@ -49,11 +51,11 @@ cargo add dioxus-web ``` Edit your `main.rs`: + ```rust {{#include ../../../examples/hello_world_web.rs}} ``` - And to serve our app: ```bash From 219af51526ffb758fbf99a4113099639bb56e398 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 3 Apr 2023 17:39:09 -0500 Subject: [PATCH 343/516] fix cargo check --- docs/guide/examples/hydration.rs | 34 +++++ docs/guide/examples/server.rs | 132 ------------------ docs/guide/examples/server_basic.rs | 30 ++++ docs/guide/examples/server_function.rs | 58 ++++++++ .../guide/src/en/getting_started/fullstack.md | 6 +- packages/router/src/components/router.rs | 7 +- packages/server/Cargo.toml | 2 +- packages/server/server-macro/Cargo.toml | 2 +- packages/server/src/lib.rs | 4 +- packages/server/src/render.rs | 3 +- 10 files changed, 136 insertions(+), 142 deletions(-) create mode 100644 docs/guide/examples/hydration.rs delete mode 100644 docs/guide/examples/server.rs create mode 100644 docs/guide/examples/server_basic.rs create mode 100644 docs/guide/examples/server_function.rs diff --git a/docs/guide/examples/hydration.rs b/docs/guide/examples/hydration.rs new file mode 100644 index 000000000..f3c9bca8f --- /dev/null +++ b/docs/guide/examples/hydration.rs @@ -0,0 +1,34 @@ +#![allow(non_snake_case, unused)] +use dioxus::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + use dioxus_server::prelude::*; + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .into_make_service(), + ) + .await + .unwrap(); + }); + } +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + }) +} diff --git a/docs/guide/examples/server.rs b/docs/guide/examples/server.rs deleted file mode 100644 index 80e74a919..000000000 --- a/docs/guide/examples/server.rs +++ /dev/null @@ -1,132 +0,0 @@ -mod basic { - // ANCHOR: basic - #![allow(non_snake_case)] - use dioxus::prelude::*; - use dioxus_server::prelude::*; - - #[tokio::main] - async fn main() { - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - axum::Server::bind(&addr) - .serve( - axum::Router::new() - .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) - .into_make_service(), - ) - .await - .unwrap(); - } - - fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || 0); - - cx.render(rsx! { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - }) - } - // ANCHOR_END: basic -} - -mod hydration { - // ANCHOR: hydration - #![allow(non_snake_case)] - use dioxus::prelude::*; - use dioxus_server::prelude::*; - - fn main() { - #[cfg(feature = "web")] - dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); - #[cfg(feature = "ssr")] - { - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - axum::Server::bind(&addr) - .serve( - axum::Router::new() - .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) - .into_make_service(), - ) - .await - .unwrap(); - }); - } - } - - fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || 0); - - cx.render(rsx! { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - }) - } - // ANCHOR_END: hydration -} - -mod server_function { - // ANCHOR: server_function - #![allow(non_snake_case)] - use dioxus::prelude::*; - use dioxus_server::prelude::*; - - fn main() { - #[cfg(feature = "web")] - dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); - #[cfg(feature = "ssr")] - { - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async move { - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); - axum::Server::bind(&addr) - .serve( - axum::Router::new() - // Serve Dioxus application automatically recognizes server functions and adds them to the API - .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) - .into_make_service(), - ) - .await - .unwrap(); - }); - } - } - - fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || 0); - - cx.render(rsx! { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - button { - onclick: move |_| { - to_owned![count]; - async move { - // Call the server function just like a local async function - if let Ok(new_count) = double_server(*count.current()).await { - count.set(new_count); - } - } - }, - "Double" - } - }) - } - - #[server(DoubleServer)] - async fn double_server(number: u32) -> Result { - // Perform some expensive computation or access a database on the server - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - let result = number * 2; - println!("server calculated {result}"); - Ok(result) - } - // ANCHOR_END: server_function -} diff --git a/docs/guide/examples/server_basic.rs b/docs/guide/examples/server_basic.rs new file mode 100644 index 000000000..bd6cf78e6 --- /dev/null +++ b/docs/guide/examples/server_basic.rs @@ -0,0 +1,30 @@ +#![allow(non_snake_case, unused)] +use dioxus::prelude::*; + +#[tokio::main] +async fn main() { + #[cfg(feature = "ssr")] + { + use dioxus_server::prelude::*; + + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .into_make_service(), + ) + .await + .unwrap(); + } +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + }) +} diff --git a/docs/guide/examples/server_function.rs b/docs/guide/examples/server_function.rs new file mode 100644 index 000000000..106c54c3f --- /dev/null +++ b/docs/guide/examples/server_function.rs @@ -0,0 +1,58 @@ +#![allow(non_snake_case, unused)] +use dioxus::prelude::*; +use dioxus_server::prelude::*; + +fn main() { + #[cfg(feature = "web")] + dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + #[cfg(feature = "ssr")] + { + // Register the server function before starting the server + DoubleServer::register().unwrap(); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + axum::Server::bind(&addr) + .serve( + axum::Router::new() + // Serve Dioxus application automatically recognizes server functions and adds them to the API + .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .into_make_service(), + ) + .await + .unwrap(); + }); + } +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || 0); + + cx.render(rsx! { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| { + to_owned![count]; + async move { + // Call the server function just like a local async function + if let Ok(new_count) = double_server(*count.current()).await { + count.set(new_count); + } + } + }, + "Double" + } + }) +} + +#[server(DoubleServer)] +async fn double_server(number: u32) -> Result { + // Perform some expensive computation or access a database on the server + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + let result = number * 2; + println!("server calculated {result}"); + Ok(result) +} diff --git a/docs/guide/src/en/getting_started/fullstack.md b/docs/guide/src/en/getting_started/fullstack.md index b132de319..f01d8dd30 100644 --- a/docs/guide/src/en/getting_started/fullstack.md +++ b/docs/guide/src/en/getting_started/fullstack.md @@ -44,7 +44,7 @@ tokio = { version = "*", features = ["full"] } Now, set up your Axum app to serve the Dioxus app. ```rust -{{#include ../../../examples/server.rs:basic}} +{{#include ../../../examples/server_basic.rs}} ``` Now, run your app with `cargo run` and open `http://localhost:8080` in your browser. You should see a server-side rendered page with a counter. @@ -77,7 +77,7 @@ web = ["dioxus-web"] Next, we need to modify our `main.rs` to use either hydrate on the client or render on the server depending on the active features. ```rust -{{#include ../../../examples/server.rs:hydration}} +{{#include ../../../examples/hydration.rs}} ``` Now, build your client-side bundle with `dioxus build --features web` and run your server with `cargo run --features ssr`. You should see the same page as before, but now you can interact with the buttons! @@ -105,7 +105,7 @@ cargo add serde Next, add the server function to your `main.rs`: ```rust -{{#include ../../../examples/server.rs:server_function}} +{{#include ../../../examples/server_function.rs}} ``` Now, build your client-side bundle with `dioxus build --features web` and run your server with `cargo run --features ssr`. You should see a new button that multiplies the count by 2. diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index b027386c5..8d5365c35 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -29,8 +29,9 @@ pub struct RouterProps<'a> { pub active_class: Option<&'a str>, /// Set the initial url. - #[props(!optional, into)] - pub initial_url: Option, + // This is Option> because we want to be able to either omit the prop or pass in Option + #[props(into)] + pub initial_url: Option>, } /// A component that conditionally renders children based on the current location of the app. @@ -46,7 +47,7 @@ pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element { RouterCfg { base_url: cx.props.base_url.map(|s| s.to_string()), active_class: cx.props.active_class.map(|s| s.to_string()), - initial_url: cx.props.initial_url.clone(), + initial_url: cx.props.initial_url.clone().flatten(), }, )) }); diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 8709cb57d..18633e97a 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"] [dependencies] # server functions server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] } -server_macro = { path = "server-macro" } +dioxus_server_macro = { path = "server-macro" } # warp warp = { version = "0.3.3", optional = true } diff --git a/packages/server/server-macro/Cargo.toml b/packages/server/server-macro/Cargo.toml index d03c5274c..45b0ec3c6 100644 --- a/packages/server/server-macro/Cargo.toml +++ b/packages/server/server-macro/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "server_macro" +name = "dioxus_server_macro" version = "0.1.0" edition = "2021" diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index c61b26429..df2735cf4 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -24,11 +24,13 @@ pub mod prelude { #[cfg(feature = "warp")] pub use crate::adapters::warp_adapter::*; #[cfg(feature = "ssr")] + pub use crate::render::SSRState; + #[cfg(feature = "ssr")] pub use crate::serve_config::{ServeConfig, ServeConfigBuilder}; pub use crate::server_context::DioxusServerContext; pub use crate::server_fn::ServerFn; #[cfg(feature = "ssr")] pub use crate::server_fn::ServerFnTraitObj; + pub use dioxus_server_macro::*; pub use server_fn::{self, ServerFn as _, ServerFnError}; - pub use server_macro::*; } diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 22e751ea8..8328be317 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -9,7 +9,7 @@ use crate::prelude::ServeConfig; /// State used in server side rendering. This utilizes a pool of [`dioxus_ssr::Renderer`]s to cache static templates between renders. #[derive(Clone)] -pub(crate) struct SSRState { +pub struct SSRState { // We keep a pool of renderers to avoid re-creating them on every request. They are boxed to make them very cheap to move renderers: Arc>, } @@ -23,6 +23,7 @@ impl Default for SSRState { } impl SSRState { + /// Render the application to HTML. pub fn render(&self, cfg: &ServeConfig

) -> String { let ServeConfig { app, props, index, .. From 44be89ff388c07bd8b3115bae714d0099ef8e457 Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:34:25 +0200 Subject: [PATCH 344/516] improve formatting with vs code extension --- extension/src/main.ts | 190 +++++++++++++++++++++++++++++----------- extension/tsconfig.json | 4 +- 2 files changed, 143 insertions(+), 51 deletions(-) diff --git a/extension/src/main.ts b/extension/src/main.ts index 1c75d3780..989220456 100644 --- a/extension/src/main.ts +++ b/extension/src/main.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import { spawn } from "child_process"; +import { TextEncoder } from 'util'; let serverPath: string = "dioxus"; @@ -19,7 +20,7 @@ export async function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand('extension.htmlToDioxusComponent', translateComponent), vscode.commands.registerCommand('extension.formatRsx', fmtSelection), vscode.commands.registerCommand('extension.formatRsxDocument', formatRsxDocument), - vscode.workspace.onWillSaveTextDocument(fmtDocument) + vscode.workspace.onWillSaveTextDocument(fmtDocumentOnSave) ); } @@ -81,9 +82,8 @@ function convertHtmlToRsxOnPaste() { function formatRsxDocument() { const editor = vscode.window.activeTextEditor; - if (editor) { - fmtDocument(); - } + if (!editor) return; + fmtDocument(editor.document); } function fmtSelection() { @@ -97,7 +97,11 @@ function fmtSelection() { return; } - const child_proc = spawn(serverPath, ["fmt", "--raw", unformatted.toString()]); + const fileDir = editor.document.fileName.slice(0, editor.document.fileName.lastIndexOf('\\')); + + const child_proc = spawn(serverPath, ["fmt", "--raw", unformatted.toString()], { + cwd: fileDir ? fileDir : undefined, + }); let result = ''; child_proc.stdout?.on('data', data => result += data); @@ -111,52 +115,140 @@ function fmtSelection() { }); } -function fmtDocument() { - const editor = vscode.window.activeTextEditor; - const document = editor?.document; - if (!document) return; - +function fmtDocumentOnSave(e: vscode.TextDocumentWillSaveEvent) { // check the settings to make sure format on save is configured - if (document.languageId === "rust" && document.uri.scheme === "file") { - const active_editor = vscode.window.activeTextEditor; - - if (active_editor?.document.fileName === document.fileName) { - const text = document.getText(); - const child_proc = spawn("dioxus", ["fmt", "-f", text]); - - let result = ''; - child_proc.stdout?.on('data', data => result += data); - - type RsxEdit = { - formatted: string, - start: number, - end: number - } - - child_proc.on('close', () => { - if (result.length > 0) { - let decoded: RsxEdit[] = JSON.parse(result); - - if (decoded.length > 0) { - active_editor.edit(editBuilder => { - decoded.map((edit) => { - editBuilder.replace(new vscode.Range( - document.positionAt(edit.start), - document.positionAt(edit.end) - ), edit.formatted); - }); - }, { - undoStopAfter: false, - undoStopBefore: false - }) - } - } - }); - - child_proc.on('error', (err) => { - vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); - }); + if (!vscode.workspace.getConfiguration('editor', e.document).get('formatOnSave')) { + return; + } + fmtDocument(e.document); +} +function fmtDocument(document: vscode.TextDocument) { + try { + if (document.languageId !== "rust" || document.uri.scheme !== "file") { + return; } + + const [editor,] = vscode.window.visibleTextEditors.filter(editor => editor.document.fileName === document.fileName); + if (!editor) return; // Need an editor to apply text edits. + + const text = document.getText(); + const fileDir = document.fileName.slice(0, document.fileName.lastIndexOf('\\')); + + const child_proc = spawn(serverPath, ["fmt", "--file", text], { + cwd: fileDir ? fileDir : undefined, + }); + + let result = ''; + child_proc.stdout?.on('data', data => result += data); + + type RsxEdit = { + formatted: string, + start: number, + end: number + } + + child_proc.on('close', () => { + if (child_proc.exitCode !== 0) { + vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed!\nDioxus-CLI exited with exit code ${child_proc.exitCode}\n\nData from Dioxus-CLI:\n${result}`); + return; + } + if (result.length === 0) return; + + // Used for error message: + const originalResult = result; + try { + // Only parse the last non empty line, to skip log warning messages: + const lines = result.replaceAll('\r\n', '\n').split('\n'); + const nonEmptyLines = lines.filter(line => line.trim().length !== 0); + result = nonEmptyLines[nonEmptyLines.length - 1] ?? ''; + + if (result.length === 0) return; + + let decoded: RsxEdit[] = JSON.parse(result); + if (decoded.length === 0) return; + + // Preform edits at the end of the file + // first (to not change previous text file + // offsets): + decoded.sort((a, b) => b.start - a.start); + + + // Convert from utf8 offsets to utf16 offsets used by VS Code: + + const utf8Text = new TextEncoder().encode(text); + const utf8ToUtf16Pos = (posUtf8: number) => { + const beforeTarget = utf8Text.slice(0, posUtf8 - 1); + + // Find the line of the position as well as the utf8 and + // utf16 indexes for the start of that line: + let startOfLineUtf8 = 0; + let lineIndex = 0; + const newLineUtf8 = '\n'.charCodeAt(0); + while (true) { + const nextLineAt = beforeTarget.indexOf(newLineUtf8, startOfLineUtf8); + if (nextLineAt < 0) break; + startOfLineUtf8 = nextLineAt + 1; + lineIndex++; + } + const lineUtf16 = document.lineAt(lineIndex); + + // Move forward from a synced position in the text until the + // target pos is found: + let currentUtf8 = startOfLineUtf8; + let currentUtf16 = document.offsetAt(lineUtf16.range.start); + + const decodeBuffer = new Uint8Array(10); + const utf8Encoder = new TextEncoder(); + while (currentUtf8 < posUtf8) { + const { written } = utf8Encoder.encodeInto(text.charAt(currentUtf16), decodeBuffer); + currentUtf8 += written; + currentUtf16++; + } + return currentUtf16; + }; + + type FixedEdit = { + range: vscode.Range, + formatted: string, + }; + + const edits: FixedEdit[] = []; + for (const edit of decoded) { + // Convert from utf8 to utf16: + const range = new vscode.Range( + document.positionAt(utf8ToUtf16Pos(edit.start)), + document.positionAt(utf8ToUtf16Pos(edit.end)) + ); + + if (editor.document.getText(range) !== document.getText(range)) { + // The text that was formatted has changed while we were working. + vscode.window.showWarningMessage(`Dioxus formatting was ignored since the source file changed before the change could be applied.`); + continue; + } + + edits.push({ + range, + formatted: edit.formatted, + }); + } + + // Apply edits: + editor.edit(editBuilder => { + edits.forEach((edit) => editBuilder.replace(edit.range, edit.formatted)); + }, { + undoStopAfter: false, + undoStopBefore: false + }) + } catch (err) { + vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed!\n${err}\n\nData from Dioxus-CLI:\n${originalResult}`); + } + }); + + child_proc.on('error', (err) => { + vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); + }); + } catch (error) { + vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed! \n${error}`); } } diff --git a/extension/tsconfig.json b/extension/tsconfig.json index 39ed78e3e..32dda5bc9 100644 --- a/extension/tsconfig.json +++ b/extension/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "module": "commonjs", - "target": "es2019", - "lib": ["ES2019"], + "target": "es2021", + "lib": ["ES2021"], "outDir": "out", "sourceMap": true, "strict": true, From bc29cf4d28816fa602edd0dbcebb73281c953713 Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:55:53 +0200 Subject: [PATCH 345/516] extension specific config for formatOnSave feature --- extension/package.json | 30 ++++++++++++++++++++++++++++-- extension/src/main.ts | 11 ++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/extension/package.json b/extension/package.json index 3e4ca4b83..11b32ec88 100644 --- a/extension/package.json +++ b/extension/package.json @@ -42,7 +42,33 @@ "command": "extension.formatRsxDocument", "title": "Dioxus: Format RSX Document" } - ] + ], + "configuration": { + "properties": { + "dioxus.formatOnSave": { + "type": [ + "string" + ], + "default": "followFormatOnSave", + "enum": [ + "followFormatOnSave", + "enabled", + "disabled" + ], + "enumItemLabels": [ + "Follow the normal formatOnSave config", + "Enabled", + "Disabled" + ], + "enumDescriptions": [ + "Only format Rsx when saving files if the editor.formatOnSave config is enabled", + "Always format Rsx when a Rust file is saved", + "Never format Rsx when a file is saved" + ], + "description": "Format RSX when a file is saved." + } + } + } }, "scripts": { "vscode:prepublish": "npm run build-base -- --minify", @@ -73,4 +99,4 @@ "dependencies": { "vsce": "^2.9.2" } -} +} \ No newline at end of file diff --git a/extension/src/main.ts b/extension/src/main.ts index 989220456..be3740c7d 100644 --- a/extension/src/main.ts +++ b/extension/src/main.ts @@ -117,11 +117,16 @@ function fmtSelection() { function fmtDocumentOnSave(e: vscode.TextDocumentWillSaveEvent) { // check the settings to make sure format on save is configured - if (!vscode.workspace.getConfiguration('editor', e.document).get('formatOnSave')) { - return; + const dioxusConfig = vscode.workspace.getConfiguration('dioxus', e.document).get('formatOnSave'); + const globalConfig = vscode.workspace.getConfiguration('editor', e.document).get('formatOnSave'); + if ( + (dioxusConfig === 'enabled') || + (dioxusConfig !== 'disabled' && globalConfig) + ) { + fmtDocument(e.document); } - fmtDocument(e.document); } + function fmtDocument(document: vscode.TextDocument) { try { if (document.languageId !== "rust" || document.uri.scheme !== "file") { From d1d0ffd912cc533346e7b13b39c45f09ade669ab Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Thu, 6 Apr 2023 05:22:14 +0200 Subject: [PATCH 346/516] fix off-by-one error for utf8 to utf16 pos conversion --- extension/src/main.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extension/src/main.ts b/extension/src/main.ts index be3740c7d..e9580d3bb 100644 --- a/extension/src/main.ts +++ b/extension/src/main.ts @@ -169,7 +169,7 @@ function fmtDocument(document: vscode.TextDocument) { if (result.length === 0) return; - let decoded: RsxEdit[] = JSON.parse(result); + const decoded: RsxEdit[] = JSON.parse(result); if (decoded.length === 0) return; // Preform edits at the end of the file @@ -182,16 +182,15 @@ function fmtDocument(document: vscode.TextDocument) { const utf8Text = new TextEncoder().encode(text); const utf8ToUtf16Pos = (posUtf8: number) => { - const beforeTarget = utf8Text.slice(0, posUtf8 - 1); - // Find the line of the position as well as the utf8 and // utf16 indexes for the start of that line: let startOfLineUtf8 = 0; let lineIndex = 0; const newLineUtf8 = '\n'.charCodeAt(0); + // eslint-disable-next-line no-constant-condition while (true) { - const nextLineAt = beforeTarget.indexOf(newLineUtf8, startOfLineUtf8); - if (nextLineAt < 0) break; + const nextLineAt = utf8Text.indexOf(newLineUtf8, startOfLineUtf8); + if (nextLineAt < 0 || posUtf8 <= nextLineAt) break; startOfLineUtf8 = nextLineAt + 1; lineIndex++; } @@ -212,6 +211,7 @@ function fmtDocument(document: vscode.TextDocument) { return currentUtf16; }; + type FixedEdit = { range: vscode.Range, formatted: string, @@ -237,13 +237,14 @@ function fmtDocument(document: vscode.TextDocument) { }); } + // Apply edits: editor.edit(editBuilder => { edits.forEach((edit) => editBuilder.replace(edit.range, edit.formatted)); }, { undoStopAfter: false, undoStopBefore: false - }) + }); } catch (err) { vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed!\n${err}\n\nData from Dioxus-CLI:\n${originalResult}`); } From f1f7517b888e9e77e612d656ee0c876717c799af Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 6 Apr 2023 11:11:08 -0500 Subject: [PATCH 347/516] Fix form events with select multiple --- packages/html/src/events/form.rs | 2 +- packages/interpreter/src/interpreter.js | 41 +++++++---------- packages/web/src/dom.rs | 59 ++++++++++++------------- 3 files changed, 45 insertions(+), 57 deletions(-) diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 361ebe2d4..6edf60448 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -10,7 +10,7 @@ pub type FormEvent = Event; pub struct FormData { pub value: String, - pub values: HashMap, + pub values: HashMap>, #[cfg_attr(feature = "serialize", serde(skip))] pub files: Option>, diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index b921c7c22..653eb15fb 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -17,8 +17,7 @@ class ListenerMap { } else { this.global[event_name].active++; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); if (!this.local[id]) { this.local[id] = {}; @@ -32,11 +31,13 @@ class ListenerMap { if (bubbles) { this.global[event_name].active--; if (this.global[event_name].active === 0) { - this.root.removeEventListener(event_name, this.global[event_name].callback); + this.root.removeEventListener( + event_name, + this.global[event_name].callback + ); delete this.global[event_name]; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); delete this.local[id][event_name]; if (this.local[id].length === 0) { @@ -143,8 +144,7 @@ class Interpreter { SetAttribute(id, field, value, ns) { if (value === null) { this.RemoveAttribute(id, field, ns); - } - else { + } else { const node = this.nodes[id]; this.SetAttributeInner(node, field, value, ns); } @@ -342,7 +342,6 @@ class Interpreter { this.RemoveEventListener(edit.id, edit.name); break; case "NewEventListener": - let bubbles = event_bubbles(edit.name); // this handler is only provided on desktop implementations since this @@ -360,7 +359,10 @@ class Interpreter { let a_element = target.closest("a"); if (a_element != null) { event.preventDefault(); - if (shouldPreventDefault !== `onclick` && a_element.getAttribute(`dioxus-prevent-default`) !== `onclick`) { + if ( + shouldPreventDefault !== `onclick` && + a_element.getAttribute(`dioxus-prevent-default`) !== `onclick` + ) { const href = a_element.getAttribute("href"); if (href !== "" && href !== null && href !== undefined) { window.ipc.postMessage( @@ -404,23 +406,10 @@ class Interpreter { target.tagName === "FORM" && (event.type === "submit" || event.type === "input") ) { - for (let x = 0; x < target.elements.length; x++) { - let element = target.elements[x]; - let name = element.getAttribute("name"); - if (name != null) { - if (element.getAttribute("type") === "checkbox") { - // @ts-ignore - contents.values[name] = element.checked ? "true" : "false"; - } else if (element.getAttribute("type") === "radio") { - if (element.checked) { - contents.values[name] = element.value; - } - } else { - // @ts-ignore - contents.values[name] = - element.value ?? element.textContent; - } - } + const formData = new FormData(target); + + for (let name of formData.keys()) { + contents.values[name] = formData.getAll(name); } } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 4d17749d8..5b56e663e 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -13,9 +13,10 @@ use dioxus_core::{ use dioxus_html::{event_bubbles, CompositionData, FormData}; use dioxus_interpreter_js::{save_template, Channel}; use futures_channel::mpsc; +use js_sys::Array; use rustc_hash::FxHashMap; use std::{any::Any, rc::Rc}; -use wasm_bindgen::{closure::Closure, JsCast}; +use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast}; use web_sys::{Document, Element, Event, HtmlElement}; use crate::Config; @@ -325,35 +326,16 @@ fn read_input_to_data(target: Element) -> Rc { // try to fill in form values if let Some(form) = target.dyn_ref::() { - let elements = form.elements(); - for x in 0..elements.length() { - let element = elements.item(x).unwrap(); - if let Some(name) = element.get_attribute("name") { - let value: Option = element - .dyn_ref() - .map(|input: &web_sys::HtmlInputElement| { - match input.type_().as_str() { - "checkbox" => { - match input.checked() { - true => Some("true".to_string()), - false => Some("false".to_string()), - } - }, - "radio" => { - match input.checked() { - true => Some(input.value()), - false => None, - } - } - _ => Some(input.value()) - } - }) - .or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlTextAreaElement| Some(input.value()))) - .or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlSelectElement| Some(input.value()))) - .or_else(|| Some(element.dyn_ref::().unwrap().text_content())) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener"); - if let Some(value) = value { - values.insert(name, value); + let form_data = get_form_data(form); + for value in form_data.entries().into_iter().flatten() { + if let Ok(array) = value.dyn_into::() { + if let Some(name) = array.get(0).as_string() { + if let Ok(item_values) = array.get(1).dyn_into::() { + let item_values = + item_values.iter().filter_map(|v| v.as_string()).collect(); + + values.insert(name, item_values); + } } } } @@ -366,6 +348,23 @@ fn read_input_to_data(target: Element) -> Rc { }) } +// web-sys does not expose the keys api for form data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" + export function get_form_data(form) { + let values = new Map(); + const formData = new FormData(form); + + for (let name of formData.keys()) { + values.set(name, formData.getAll(name)); + } + + return values; + } +"#)] +extern "C" { + fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; +} + fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> { let mut target = event .target() From a590a07b3fe92c26ce17ddd5718ed1fbb63f13a1 Mon Sep 17 00:00:00 2001 From: Anatol Ulrich <45840+spookyvision@users.noreply.github.com> Date: Fri, 7 Apr 2023 17:19:31 +0200 Subject: [PATCH 348/516] propagate `features` to `dioxus serve` #122 fixed `--features` for `dioxus build`, this PR adds the same for `dioxus serve` --- src/cli/serve/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 841e72d10..ce0dd743e 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -30,6 +30,10 @@ impl Serve { if self.serve.profile.is_some() { crate_config.set_profile(self.serve.profile.unwrap()); } + + if self.serve.features.is_some() { + crate_config.set_features(self.serve.features.unwrap()); + } let platform = self.serve.platform.unwrap_or_else(|| { crate_config From a5c46b0e5ce6acd2594f93bde35a5c486f35a828 Mon Sep 17 00:00:00 2001 From: Andrew Collins <50530928+ProfXwing@users.noreply.github.com> Date: Sat, 8 Apr 2023 11:23:48 -0400 Subject: [PATCH 349/516] new window returns desktop context --- packages/desktop/src/desktop_context.rs | 9 +++----- packages/desktop/src/lib.rs | 30 +++++++++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 2ed4bbee9..03166d272 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -1,6 +1,5 @@ use std::cell::RefCell; use std::rc::Rc; -use std::rc::Weak; use crate::create_new_window; use crate::eval::EvalResult; @@ -112,8 +111,8 @@ impl DesktopContext { /// You can use this to control other windows from the current window. /// /// Be careful to not create a cycle of windows, or you might leak memory. - pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak { - let window = create_new_window( + pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> DesktopContext { + let (window, desktop_context) = create_new_window( cfg, &self.event_loop, &self.proxy, @@ -133,11 +132,9 @@ impl DesktopContext { .send_event(UserWindowEvent(EventData::Poll, id)) .unwrap(); - let webview = window.webview.clone(); - self.pending_windows.borrow_mut().push(window); - Rc::downgrade(&webview) + desktop_context } /// trigger the drag-window event diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 75c67dd22..0a9a8558c 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -144,8 +144,7 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) let shortcut_manager = ShortcutRegistry::new(&event_loop); - // By default, we'll create a new window when the app starts - queue.borrow_mut().push(create_new_window( + let (web_view, _) = create_new_window( cfg, &event_loop, &proxy, @@ -153,7 +152,10 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) &queue, &event_handlers, shortcut_manager.clone(), - )); + ); + + // By default, we'll create a new window when the app starts + queue.borrow_mut().push(web_view); event_loop.run(move |window_event, event_loop, control_flow| { *control_flow = ControlFlow::Wait; @@ -280,26 +282,30 @@ fn create_new_window( queue: &WebviewQueue, event_handlers: &WindowEventHandlers, shortcut_manager: ShortcutRegistry, -) -> WebviewHandler { +) -> (WebviewHandler, DesktopContext) { let webview = webview::build(&mut cfg, event_loop, proxy.clone()); - - dom.base_scope().provide_context(DesktopContext::new( + let desktop_context = DesktopContext::new( webview.clone(), proxy.clone(), event_loop.clone(), queue.clone(), event_handlers.clone(), shortcut_manager, - )); + ); + + dom.base_scope().provide_context(desktop_context.clone()); let id = webview.window().id(); // We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both - WebviewHandler { - webview, - dom, - waker: waker::tao_waker(proxy, id), - } + ( + WebviewHandler { + webview, + dom, + waker: waker::tao_waker(proxy, id), + }, + desktop_context, + ) } struct WebviewHandler { From d005f3481fda98e50bb107d1cd699c2bd88e23a9 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 10 Apr 2023 09:45:16 -0500 Subject: [PATCH 350/516] fix non-bubbling event propagation --- packages/core/src/virtual_dom.rs | 96 ++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 2269815aa..da5526b32 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -384,51 +384,73 @@ impl VirtualDom { data, }; - // Loop through each dynamic attribute in this template before moving up to the template's parent. - while let Some(el_ref) = parent_path { - // safety: we maintain references of all vnodes in the element slab - let template = unsafe { el_ref.template.unwrap().as_ref() }; - let node_template = template.template.get(); - let target_path = el_ref.path; + // If the event bubbles, we traverse through the tree until we find the target element. + if bubbles { + // Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent. + while let Some(el_ref) = parent_path { + // safety: we maintain references of all vnodes in the element slab + let template = unsafe { el_ref.template.unwrap().as_ref() }; + let node_template = template.template.get(); + let target_path = el_ref.path; - for (idx, attr) in template.dynamic_attrs.iter().enumerate() { - let this_path = node_template.attr_paths[idx]; + for (idx, attr) in template.dynamic_attrs.iter().enumerate() { + let this_path = node_template.attr_paths[idx]; - // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one - if attr.name.trim_start_matches("on") == name - && target_path.is_decendant(&this_path) - { - listeners.push(&attr.value); + // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one + if attr.name.trim_start_matches("on") == name + && target_path.is_decendant(&this_path) + { + listeners.push(&attr.value); - // Break if the event doesn't bubble anyways - if !bubbles { - break; + // Break if this is the exact target element. + // This means we won't call two listeners with the same name on the same element. This should be + // documented, or be rejected from the rsx! macro outright + if target_path == this_path { + break; + } } + } - // Break if this is the exact target element. - // This means we won't call two listeners with the same name on the same element. This should be - // documented, or be rejected from the rsx! macro outright - if target_path == this_path { - break; + // Now that we've accumulated all the parent attributes for the target element, call them in reverse order + // We check the bubble state between each call to see if the event has been stopped from bubbling + for listener in listeners.drain(..).rev() { + if let AttributeValue::Listener(listener) = listener { + if let Some(cb) = listener.borrow_mut().as_deref_mut() { + cb(uievent.clone()); + } + + if !uievent.propagates.get() { + return; + } + } + } + + parent_path = template.parent.and_then(|id| self.elements.get(id.0)); + } + } else { + // Otherwise, we just call the listener on the target element + if let Some(el_ref) = parent_path { + // safety: we maintain references of all vnodes in the element slab + let template = unsafe { el_ref.template.unwrap().as_ref() }; + let node_template = template.template.get(); + let target_path = el_ref.path; + + for (idx, attr) in template.dynamic_attrs.iter().enumerate() { + let this_path = node_template.attr_paths[idx]; + + // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one + // Only call the listener if this is the exact target element. + if attr.name.trim_start_matches("on") == name && target_path == this_path { + if let AttributeValue::Listener(listener) = &attr.value { + if let Some(cb) = listener.borrow_mut().as_deref_mut() { + cb(uievent.clone()); + } + + break; + } } } } - - // Now that we've accumulated all the parent attributes for the target element, call them in reverse order - // We check the bubble state between each call to see if the event has been stopped from bubbling - for listener in listeners.drain(..).rev() { - if let AttributeValue::Listener(listener) = listener { - if let Some(cb) = listener.borrow_mut().as_deref_mut() { - cb(uievent.clone()); - } - - if !uievent.propagates.get() { - return; - } - } - } - - parent_path = template.parent.and_then(|id| self.elements.get(id.0)); } } From 0e1f015b78e82e28e4a1889eaa6552c6ef981ee1 Mon Sep 17 00:00:00 2001 From: Andrew Collins <50530928+ProfXwing@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:03:46 -0400 Subject: [PATCH 351/516] turn desktop context into Rc --- packages/desktop/src/desktop_context.rs | 19 +++++++++++++------ packages/desktop/src/lib.rs | 22 ++++++++++------------ packages/desktop/src/shortcut.rs | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 03166d272..35da2eab3 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::rc::Rc; +use std::rc::Weak; use crate::create_new_window; use crate::eval::EvalResult; @@ -30,8 +31,8 @@ use wry::webview::WebView; pub type ProxyType = EventLoopProxy; /// Get an imperative handle to the current window -pub fn use_window(cx: &ScopeState) -> &DesktopContext { - cx.use_hook(|| cx.consume_context::()) +pub fn use_window(cx: &ScopeState) -> &Rc { + cx.use_hook(|| cx.consume_context::>()) .as_ref() .unwrap() } @@ -111,8 +112,8 @@ impl DesktopContext { /// You can use this to control other windows from the current window. /// /// Be careful to not create a cycle of windows, or you might leak memory. - pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> DesktopContext { - let (window, desktop_context) = create_new_window( + pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak { + let window = create_new_window( cfg, &self.event_loop, &self.proxy, @@ -122,6 +123,12 @@ impl DesktopContext { self.shortcut_manager.clone(), ); + let desktop_context = window + .dom + .base_scope() + .consume_context::>() + .unwrap(); + let id = window.webview.window().id(); self.proxy @@ -134,7 +141,7 @@ impl DesktopContext { self.pending_windows.borrow_mut().push(window); - desktop_context + Rc::downgrade(&desktop_context) } /// trigger the drag-window event @@ -420,7 +427,7 @@ pub fn use_wry_event_handler( let id = desktop.create_wry_event_handler(handler); WryEventHandler { - handlers: desktop.event_handlers, + handlers: desktop.event_handlers.clone(), id, } }) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 0a9a8558c..2a6f5aa6b 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -144,7 +144,7 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) let shortcut_manager = ShortcutRegistry::new(&event_loop); - let (web_view, _) = create_new_window( + let web_view = create_new_window( cfg, &event_loop, &proxy, @@ -282,30 +282,28 @@ fn create_new_window( queue: &WebviewQueue, event_handlers: &WindowEventHandlers, shortcut_manager: ShortcutRegistry, -) -> (WebviewHandler, DesktopContext) { +) -> WebviewHandler { let webview = webview::build(&mut cfg, event_loop, proxy.clone()); - let desktop_context = DesktopContext::new( + let desktop_context = Rc::from(DesktopContext::new( webview.clone(), proxy.clone(), event_loop.clone(), queue.clone(), event_handlers.clone(), shortcut_manager, - ); + )); dom.base_scope().provide_context(desktop_context.clone()); let id = webview.window().id(); // We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both - ( - WebviewHandler { - webview, - dom, - waker: waker::tao_waker(proxy, id), - }, - desktop_context, - ) + + WebviewHandler { + webview, + dom, + waker: waker::tao_waker(proxy, id), + } } struct WebviewHandler { diff --git a/packages/desktop/src/shortcut.rs b/packages/desktop/src/shortcut.rs index 5232bdb8b..e70d13235 100644 --- a/packages/desktop/src/shortcut.rs +++ b/packages/desktop/src/shortcut.rs @@ -146,7 +146,7 @@ pub struct ShortcutId { /// A global shortcut. This will be automatically removed when it is dropped. pub struct ShortcutHandle { - desktop: DesktopContext, + desktop: Rc, /// The id of the shortcut pub shortcut_id: ShortcutId, } From 64575eaee24fec9d10545648aa7d74f1c766de53 Mon Sep 17 00:00:00 2001 From: Andrew Collins <50530928+ProfXwing@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:06:16 -0400 Subject: [PATCH 352/516] forgot one --- packages/desktop/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 2a6f5aa6b..270eb1580 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -248,7 +248,7 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) webviews[&event.1] .dom .base_scope() - .consume_context::() + .consume_context::>() .unwrap() .eval .send(msg.params()) From d6b9d8f5b65fc0931bd5381ea967fb7f7d487a74 Mon Sep 17 00:00:00 2001 From: Andrew Collins <50530928+ProfXwing@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:45:00 -0400 Subject: [PATCH 353/516] remove rc around webview and add type for desktopcontext --- packages/desktop/src/desktop_context.rs | 23 +++++++++++----------- packages/desktop/src/lib.rs | 26 +++++++++++++------------ packages/desktop/src/shortcut.rs | 4 ++-- packages/desktop/src/webview.rs | 6 ++---- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 35da2eab3..effd9acd1 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -31,8 +31,8 @@ use wry::webview::WebView; pub type ProxyType = EventLoopProxy; /// Get an imperative handle to the current window -pub fn use_window(cx: &ScopeState) -> &Rc { - cx.use_hook(|| cx.consume_context::>()) +pub fn use_window(cx: &ScopeState) -> &DesktopContext { + cx.use_hook(|| cx.consume_context::()) .as_ref() .unwrap() } @@ -51,10 +51,9 @@ pub(crate) type WebviewQueue = Rc>>; /// ```rust, ignore /// let desktop = cx.consume_context::().unwrap(); /// ``` -#[derive(Clone)] -pub struct DesktopContext { +pub struct DesktopService { /// The wry/tao proxy to the current window - pub webview: Rc, + pub webview: WebView, /// The proxy to the event loop pub proxy: ProxyType, @@ -74,8 +73,10 @@ pub struct DesktopContext { pub(crate) views: Rc>>, } +pub type DesktopContext = Rc; + /// A smart pointer to the current window. -impl std::ops::Deref for DesktopContext { +impl std::ops::Deref for DesktopService { type Target = Window; fn deref(&self) -> &Self::Target { @@ -83,9 +84,9 @@ impl std::ops::Deref for DesktopContext { } } -impl DesktopContext { +impl DesktopService { pub(crate) fn new( - webview: Rc, + webview: WebView, proxy: ProxyType, event_loop: EventLoopWindowTarget, webviews: WebviewQueue, @@ -112,7 +113,7 @@ impl DesktopContext { /// You can use this to control other windows from the current window. /// /// Be careful to not create a cycle of windows, or you might leak memory. - pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak { + pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak { let window = create_new_window( cfg, &self.event_loop, @@ -126,10 +127,10 @@ impl DesktopContext { let desktop_context = window .dom .base_scope() - .consume_context::>() + .consume_context::>() .unwrap(); - let id = window.webview.window().id(); + let id = window.desktop_context.webview.window().id(); self.proxy .send_event(UserWindowEvent(EventData::NewWindow, id)) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 270eb1580..fe8ba237e 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -15,9 +15,11 @@ mod webview; pub use cfg::Config; pub use desktop_context::{ - use_window, use_wry_event_handler, DesktopContext, WryEventHandler, WryEventHandlerId, + use_window, use_wry_event_handler, DesktopService, WryEventHandler, WryEventHandlerId, +}; +use desktop_context::{ + DesktopContext, EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers, }; -use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers}; use dioxus_core::*; use dioxus_html::HtmlEvent; pub use eval::{use_eval, EvalResult}; @@ -186,7 +188,7 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) Event::NewEvents(StartCause::Init) | Event::UserEvent(UserWindowEvent(EventData::NewWindow, _)) => { for handler in queue.borrow_mut().drain(..) { - let id = handler.webview.window().id(); + let id = handler.desktop_context.webview.window().id(); webviews.insert(id, handler); _ = proxy.send_event(UserWindowEvent(EventData::Poll, id)); } @@ -232,12 +234,12 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) view.dom .handle_event(&evt.name, evt.data.into_any(), evt.element, evt.bubbles); - send_edits(view.dom.render_immediate(), &view.webview); + send_edits(view.dom.render_immediate(), &view.desktop_context.webview); } EventData::Ipc(msg) if msg.method() == "initialize" => { let view = webviews.get_mut(&event.1).unwrap(); - send_edits(view.dom.rebuild(), &view.webview); + send_edits(view.dom.rebuild(), &view.desktop_context.webview); } // When the webview chirps back with the result of the eval, we send it to the active receiver @@ -248,7 +250,7 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) webviews[&event.1] .dom .base_scope() - .consume_context::>() + .consume_context::() .unwrap() .eval .send(msg.params()) @@ -284,8 +286,8 @@ fn create_new_window( shortcut_manager: ShortcutRegistry, ) -> WebviewHandler { let webview = webview::build(&mut cfg, event_loop, proxy.clone()); - let desktop_context = Rc::from(DesktopContext::new( - webview.clone(), + let desktop_context = Rc::from(DesktopService::new( + webview, proxy.clone(), event_loop.clone(), queue.clone(), @@ -295,12 +297,12 @@ fn create_new_window( dom.base_scope().provide_context(desktop_context.clone()); - let id = webview.window().id(); + let id = desktop_context.webview.window().id(); // We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both WebviewHandler { - webview, + desktop_context, dom, waker: waker::tao_waker(proxy, id), } @@ -308,7 +310,7 @@ fn create_new_window( struct WebviewHandler { dom: VirtualDom, - webview: Rc, + desktop_context: DesktopContext, waker: Waker, } @@ -331,7 +333,7 @@ fn poll_vdom(view: &mut WebviewHandler) { } } - send_edits(view.dom.render_immediate(), &view.webview); + send_edits(view.dom.render_immediate(), &view.desktop_context.webview); } } diff --git a/packages/desktop/src/shortcut.rs b/packages/desktop/src/shortcut.rs index e70d13235..8d1c09d1a 100644 --- a/packages/desktop/src/shortcut.rs +++ b/packages/desktop/src/shortcut.rs @@ -10,7 +10,7 @@ use wry::application::{ keyboard::{KeyCode, ModifiersState}, }; -use crate::{use_window, DesktopContext}; +use crate::{desktop_context::DesktopContext, use_window}; #[derive(Clone)] pub(crate) struct ShortcutRegistry { @@ -146,7 +146,7 @@ pub struct ShortcutId { /// A global shortcut. This will be automatically removed when it is dropped. pub struct ShortcutHandle { - desktop: Rc, + desktop: DesktopContext, /// The id of the shortcut pub shortcut_id: ShortcutId, } diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 099a2bd94..8e1687374 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use crate::desktop_context::EventData; use crate::protocol; use crate::{desktop_context::UserWindowEvent, Config}; @@ -13,7 +11,7 @@ pub fn build( cfg: &mut Config, event_loop: &EventLoopWindowTarget, proxy: EventLoopProxy, -) -> Rc { +) -> WebView { let builder = cfg.window.clone(); let window = builder.build(event_loop).unwrap(); let file_handler = cfg.file_drop_handler.take(); @@ -82,5 +80,5 @@ pub fn build( webview = webview.with_devtools(true); } - Rc::new(webview.build().unwrap()) + webview.build().unwrap() } From bed1d58a77f420a1c0f7303f1c48fcfbe0c6e5ac Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 18 Apr 2023 09:24:34 -0500 Subject: [PATCH 354/516] create web file engine --- packages/web/Cargo.toml | 4 ++ packages/web/src/dom.rs | 20 +++++-- packages/web/src/file_engine.rs | 103 ++++++++++++++++++++++++++++++++ packages/web/src/lib.rs | 1 + 4 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 packages/web/src/file_engine.rs diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 7adfbf13a..fcbb996bb 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -32,6 +32,7 @@ futures-channel = "0.3.21" serde_json = { version = "1.0" } serde = { version = "1.0" } serde-wasm-bindgen = "0.4.5" +async-trait = "0.1.58" [dependencies.web-sys] version = "0.3.56" @@ -76,6 +77,9 @@ features = [ "Location", "MessageEvent", "console", + "FileList", + "File", + "FileReader" ] [features] diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 4d17749d8..e792c977d 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -10,15 +10,15 @@ use dioxus_core::{ BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode, }; -use dioxus_html::{event_bubbles, CompositionData, FormData}; +use dioxus_html::{event_bubbles, CompositionData, FileEngine, FormData}; use dioxus_interpreter_js::{save_template, Channel}; use futures_channel::mpsc; use rustc_hash::FxHashMap; -use std::{any::Any, rc::Rc}; +use std::{any::Any, rc::Rc, sync::Arc}; use wasm_bindgen::{closure::Closure, JsCast}; -use web_sys::{Document, Element, Event, HtmlElement}; +use web_sys::{console, Document, Element, Event, HtmlElement}; -use crate::Config; +use crate::{file_engine::WebFileEngine, Config}; pub struct WebsysDom { document: Document, @@ -206,6 +206,7 @@ impl WebsysDom { }, SetText { value, id } => i.set_text(id.0 as u32, value), NewEventListener { name, id, .. } => { + console::log_1(&format!("new event listener: {}", name).into()); i.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8); } RemoveEventListener { name, id } => { @@ -224,6 +225,7 @@ impl WebsysDom { // We need tests that simulate clicks/etc and make sure every event type works. pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc { use dioxus_html::events::*; + console::log_1(&event.clone().into()); match event.type_().as_str() { "copy" | "cut" | "paste" => Rc::new(ClipboardData {}), @@ -359,10 +361,18 @@ fn read_input_to_data(target: Element) -> Rc { } } + let files = target + .dyn_ref() + .and_then(|input: &web_sys::HtmlInputElement| { + input.files().and_then(|files| { + WebFileEngine::new(files).map(|f| Arc::new(f) as Arc) + }) + }); + Rc::new(FormData { value, values, - files: None, + files, }) } diff --git a/packages/web/src/file_engine.rs b/packages/web/src/file_engine.rs new file mode 100644 index 000000000..6552be084 --- /dev/null +++ b/packages/web/src/file_engine.rs @@ -0,0 +1,103 @@ +use dioxus_html::FileEngine; +use futures_channel::oneshot; +use js_sys::Uint8Array; +use wasm_bindgen::{prelude::Closure, JsCast}; +use web_sys::{File, FileList, FileReader}; + +pub(crate) struct WebFileEngine { + file_reader: FileReader, + file_list: FileList, +} + +impl WebFileEngine { + pub fn new(file_list: FileList) -> Option { + Some(Self { + file_list, + file_reader: FileReader::new().ok()?, + }) + } + + fn len(&self) -> usize { + self.file_list.length() as usize + } + + fn get(&self, index: usize) -> Option { + self.file_list.item(index as u32) + } + + fn find(&self, name: &str) -> Option { + (0..self.len()) + .filter_map(|i| self.get(i)) + .find(|f| f.name() == name) + } +} + +#[async_trait::async_trait(?Send)] +impl FileEngine for WebFileEngine { + fn files(&self) -> Vec { + (0..self.len()) + .filter_map(|i| self.get(i).map(|f| f.name())) + .collect() + } + + // read a file to bytes + async fn read_file(&self, file: &str) -> Option> { + let file = self.find(file)?; + + let file_reader = self.file_reader.clone(); + let (rx, tx) = oneshot::channel(); + let on_load: Closure = Closure::new({ + let mut rx = Some(rx); + move || { + let result = file_reader.result(); + let _ = rx + .take() + .expect("multiple files read without refreshing the channel") + .send(result); + } + }); + + self.file_reader + .set_onload(Some(on_load.as_ref().unchecked_ref())); + on_load.forget(); + self.file_reader.read_as_array_buffer(&file).ok()?; + + if let Ok(Ok(js_val)) = tx.await { + let as_u8_arr = Uint8Array::new(&js_val); + let as_u8_vec = as_u8_arr.to_vec(); + + Some(as_u8_vec) + } else { + None + } + } + + // read a file to string + async fn read_file_to_string(&self, file: &str) -> Option { + let file = self.find(file)?; + + let file_reader = self.file_reader.clone(); + let (rx, tx) = oneshot::channel(); + let on_load: Closure = Closure::new({ + let mut rx = Some(rx); + move || { + let result = file_reader.result(); + let _ = rx + .take() + .expect("multiple files read without refreshing the channel") + .send(result); + } + }); + + self.file_reader + .set_onload(Some(on_load.as_ref().unchecked_ref())); + on_load.forget(); + self.file_reader.read_as_text(&file).ok()?; + + if let Ok(Ok(js_val)) = tx.await { + js_val.as_string() + } else { + None + } + } +} diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index e208b0591..14705b85a 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -61,6 +61,7 @@ use futures_util::{pin_mut, FutureExt, StreamExt}; mod cache; mod cfg; mod dom; +mod file_engine; mod hot_reload; #[cfg(feature = "hydrate")] mod rehydrate; From 8669a5eb0e154a861ab4b9f67df1e859401ee783 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 20 Apr 2023 08:55:35 -0500 Subject: [PATCH 355/516] remove unused shipyard-heigharchy --- packages/native-core/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 9ea6d4c9f..c9be2f27d 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -29,7 +29,6 @@ lightningcss = "1.0.0-alpha.39" rayon = "1.6.1" shipyard = { version = "0.6.2", features = ["proc", "std"], default-features = false } -shipyard_hierarchy = "0.6.0" [dev-dependencies] rand = "0.8.5" From d7eae795094f810d27833f3efd0d22696f347a42 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 20 Apr 2023 10:10:52 -0500 Subject: [PATCH 356/516] prevent default file input behavior on desktop --- packages/desktop/src/protocol.rs | 18 +- packages/interpreter/src/interpreter.js | 208 ++++++++++++------------ 2 files changed, 124 insertions(+), 102 deletions(-) diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index efdc849a5..3d4246987 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -9,10 +9,26 @@ use wry::{ }; fn module_loader(root_name: &str) -> String { + let js = INTERPRETER_JS.replace( + "/*POST_HANDLE_EDITS*/", + r#"// Prevent file inputs from opening the file dialog on click + let inputs = document.querySelectorAll("input"); + for (let input of inputs) { + input.addEventListener("click", (event) => { + let target = event.target; + // prevent file inputs from opening the file dialog on click + const type = target.getAttribute("type"); + if (type === "file") { + window.ipc.postMessage(serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple") })); + event.preventDefault(); + } + }); + }"#, + ); format!( r#" diff --git a/packages/interpreter/src/bindings.rs b/packages/interpreter/src/bindings.rs index 478b8ca5c..4fd9016f1 100644 --- a/packages/interpreter/src/bindings.rs +++ b/packages/interpreter/src/bindings.rs @@ -6,10 +6,14 @@ use web_sys::Element; #[wasm_bindgen(module = "/src/interpreter.js")] extern "C" { + pub type InterpreterConfig; + #[wasm_bindgen(constructor)] + pub fn new(intercept_link_redirects: bool) -> InterpreterConfig; + pub type Interpreter; #[wasm_bindgen(constructor)] - pub fn new(arg: Element) -> Interpreter; + pub fn new(arg: Element, config: InterpreterConfig) -> Interpreter; #[wasm_bindgen(method)] pub fn SaveTemplate(this: &Interpreter, template: JsValue); diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index b921c7c22..df9dd85e0 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -17,8 +17,7 @@ class ListenerMap { } else { this.global[event_name].active++; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); if (!this.local[id]) { this.local[id] = {}; @@ -32,11 +31,13 @@ class ListenerMap { if (bubbles) { this.global[event_name].active--; if (this.global[event_name].active === 0) { - this.root.removeEventListener(event_name, this.global[event_name].callback); + this.root.removeEventListener( + event_name, + this.global[event_name].callback + ); delete this.global[event_name]; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); delete this.local[id][event_name]; if (this.local[id].length === 0) { @@ -52,8 +53,15 @@ class ListenerMap { } } +class InterpreterConfig { + constructor(intercept_link_redirects) { + this.intercept_link_redirects = intercept_link_redirects; + } +} + class Interpreter { - constructor(root) { + constructor(root, config) { + this.config = config; this.root = root; this.listeners = new ListenerMap(root); this.nodes = [root]; @@ -143,8 +151,7 @@ class Interpreter { SetAttribute(id, field, value, ns) { if (value === null) { this.RemoveAttribute(id, field, ns); - } - else { + } else { const node = this.nodes[id]; this.SetAttributeInner(node, field, value, ns); } @@ -342,7 +349,6 @@ class Interpreter { this.RemoveEventListener(edit.id, edit.name); break; case "NewEventListener": - let bubbles = event_bubbles(edit.name); // this handler is only provided on desktop implementations since this @@ -357,15 +363,21 @@ class Interpreter { if (event.type === "click") { // todo call prevent default if it's the right type of event - let a_element = target.closest("a"); - if (a_element != null) { - event.preventDefault(); - if (shouldPreventDefault !== `onclick` && a_element.getAttribute(`dioxus-prevent-default`) !== `onclick`) { - const href = a_element.getAttribute("href"); - if (href !== "" && href !== null && href !== undefined) { - window.ipc.postMessage( - serializeIpcMessage("browser_open", { href }) - ); + if (this.config.intercept_link_redirects) { + let a_element = target.closest("a"); + if (a_element != null) { + event.preventDefault(); + if ( + shouldPreventDefault !== `onclick` && + a_element.getAttribute(`dioxus-prevent-default`) !== + `onclick` + ) { + const href = a_element.getAttribute("href"); + if (href !== "" && href !== null && href !== undefined) { + window.ipc.postMessage( + serializeIpcMessage("browser_open", { href }) + ); + } } } } diff --git a/packages/liveview/src/main.js b/packages/liveview/src/main.js index 7dd9758d6..a3e5136ca 100644 --- a/packages/liveview/src/main.js +++ b/packages/liveview/src/main.js @@ -8,7 +8,7 @@ function main() { class IPC { constructor(root) { // connect to the websocket - window.interpreter = new Interpreter(root); + window.interpreter = new Interpreter(root, new InterpreterConfig(false)); let ws = new WebSocket(WS_ADDR); From 459d8d69a7c35f3c9d3dc3df8171b2daf14fe422 Mon Sep 17 00:00:00 2001 From: Antonio Curavalea Date: Mon, 24 Apr 2023 02:26:23 +0300 Subject: [PATCH 364/516] examples: add PWA w/ Dioxus CLI template (#977) * examples: add PWA w/ Dioxus CLI template * ci: properly set workspace and dependencies --- Cargo.toml | 1 + examples/PWA-example/Cargo.toml | 17 ++ examples/PWA-example/Dioxus.toml | 42 +++++ examples/PWA-example/LICENSE | 21 +++ examples/PWA-example/README.md | 44 +++++ examples/PWA-example/index.html | 30 ++++ examples/PWA-example/public/favicon.ico | Bin 0 -> 23104 bytes examples/PWA-example/public/logo_192.png | Bin 0 -> 11198 bytes examples/PWA-example/public/logo_512.png | Bin 0 -> 48151 bytes examples/PWA-example/public/manifest.json | 34 ++++ examples/PWA-example/public/sw.js | 198 ++++++++++++++++++++++ examples/PWA-example/src/main.rs | 20 +++ 12 files changed, 407 insertions(+) create mode 100644 examples/PWA-example/Cargo.toml create mode 100644 examples/PWA-example/Dioxus.toml create mode 100644 examples/PWA-example/LICENSE create mode 100644 examples/PWA-example/README.md create mode 100644 examples/PWA-example/index.html create mode 100644 examples/PWA-example/public/favicon.ico create mode 100644 examples/PWA-example/public/logo_192.png create mode 100644 examples/PWA-example/public/logo_512.png create mode 100644 examples/PWA-example/public/manifest.json create mode 100644 examples/PWA-example/public/sw.js create mode 100644 examples/PWA-example/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 7b6a08a39..377ff37b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "packages/signals", "packages/hot-reload", "docs/guide", + "examples/PWA-example", ] # This is a "virtual package" diff --git a/examples/PWA-example/Cargo.toml b/examples/PWA-example/Cargo.toml new file mode 100644 index 000000000..f11d80713 --- /dev/null +++ b/examples/PWA-example/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dioxus-pwa-example" +version = "0.1.0" +authors = ["Antonio Curavalea "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dioxus = { path = "../../packages/dioxus", version = "^0.3.0"} +dioxus-web = { path = "../../packages/web", version = "^0.3.0"} + +log = "0.4.6" + +# WebAssembly Debug +wasm-logger = "0.2.0" +console_error_panic_hook = "0.1.7" diff --git a/examples/PWA-example/Dioxus.toml b/examples/PWA-example/Dioxus.toml new file mode 100644 index 000000000..e3a472520 --- /dev/null +++ b/examples/PWA-example/Dioxus.toml @@ -0,0 +1,42 @@ +[application] + +# App (Project) Name +name = "dioxus-pwa-example" + +# Dioxus App Default Platform +# desktop, web, mobile, ssr +default_platform = "web" + +# `build` & `serve` dist path +out_dir = "dist" + +# resource (public) file folder +asset_dir = "public" + +[web.app] + +# HTML title tag content +title = "dioxus | ⛺" + +[web.watcher] + +# when watcher trigger, regenerate the `index.html` +reload_html = true + +# which files or dirs will be watcher monitoring +watch_path = ["src", "public"] + +# 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 = [] diff --git a/examples/PWA-example/LICENSE b/examples/PWA-example/LICENSE new file mode 100644 index 000000000..bcdd828e9 --- /dev/null +++ b/examples/PWA-example/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dioxus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/PWA-example/README.md b/examples/PWA-example/README.md new file mode 100644 index 000000000..dfd986302 --- /dev/null +++ b/examples/PWA-example/README.md @@ -0,0 +1,44 @@ +# Dioxus PWA example + +This is a basic example of a progressive web app (PWA) using Dioxus and Dioxus CLI. +Currently PWA functionality requires the use of a service worker and manifest file, so this isn't 100% Rust yet. + +It is also very much usable as a template for your projects, if you're aiming to create a PWA. + +## Try the example + +Make sure you have Dioxus CLI installed (if you're unsure, run `cargo install dioxus-cli`). + +You can run `dioxus serve` in this directory to start the web server locally, or run +`dioxus build --release` to build the project so you can deploy it on a separate web-server. + +## Project Structure +``` +├── Cargo.toml +├── Dioxus.toml +├── index.html // Custom HTML is needed for this, to load the SW and manifest. +├── LICENSE +├── public +│ ├── favicon.ico +│ ├── logo_192.png +│ ├── logo_512.png +│ ├── manifest.json // The manifest file - edit this as you need to. +│ └── sw.js // The service worker - you must edit this for actual projects. +├── README.md +└── src + └── main.rs +``` + +## Resources + +If you're just getting started with PWAs, here are some useful resources: + +* [PWABuilder docs](https://docs.pwabuilder.com/#/) +* [MDN article on PWAs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) + +For service worker scripting (in JavaScript): + +* [Service worker guide from PWABuilder](https://docs.pwabuilder.com/#/home/sw-intro) +* [Service worker examples, also from PWABuilder](https://github.com/pwa-builder/pwabuilder-serviceworkers) + +If you want to stay as close to 100% Rust as possible, you can try using [wasi-worker](https://github.com/dunnock/wasi-worker) to replace the JS service worker file. The JSON manifest will still be required though. \ No newline at end of file diff --git a/examples/PWA-example/index.html b/examples/PWA-example/index.html new file mode 100644 index 000000000..44bfa59cc --- /dev/null +++ b/examples/PWA-example/index.html @@ -0,0 +1,30 @@ + + + + {app_title} + + + + + + {style_include} + + +

+ + {script_include} + + \ No newline at end of file diff --git a/examples/PWA-example/public/favicon.ico b/examples/PWA-example/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b11015bdbacdb3b99e03555edb85d78467b37c7d GIT binary patch literal 23104 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 W+`s-{ME)24jHW23DqAIO8vH-~t)grI literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_192.png b/examples/PWA-example/public/logo_192.png new file mode 100644 index 0000000000000000000000000000000000000000..112eb5ee49ca4e69d742a99269d1ae08a970dd7a GIT binary patch literal 11198 zcmb_?Ra6{Lu=nEb?hxF9yDSody9Os%aCdii2?TcwED+olcMBfe-JQiQ|Htp)d%5R! z&vc!sIaSkLReiep*Au0pB#nklj0^w(&}3!4sD04x|87M1k5+Bf7U6@yT8b-*0|0di zC@&^(AJ3GgGHQwdfHxfg5Eu#oKt5Q3hX8;ZI{SC@)^YwqeYlRo)x6;R!k;HnDoy}g6~<^prouqOt?fX9 zL|F^nQVA5+21WT=`p7IQqFnI{1)58Jv!N1 z{?CdT)b|eN%iNa>i-Sr=Sl9EgE~RPLmhF_CwdGjHqpc^~Bd({>7vc0&&qUJ~KPc2+ zyaCHt;@YrA0dr})&a9c&s0kdh!*Iah(=HIDTV!U$&}2n5s;`EYfD?tX=rGVZL6 zDtv;{#Oh5yR7#@~L#}l;(H*geqA>-?qJqK-`L7Ue*gG12aU*tDA@kVDgVoJtYKs`H zh%32n7Ic!={um9B|8P)BmeWPgfqlZ1kd$KpkzFiU!0H7vO)KgNI@Q*KQEd+LGk*{h zpPUUWrL5t1%LUmDCC*gj&egGo^m5kzzOuLb%5crZATf)o8Jfc+8JJdUuF&{r1KO+^ zV2ffaG`f_h(J^4`5U4zXKuv#d8=+vQL!X}hHAtG#u@Y4(k(ZU|u1!8)vKWNoEd*c7 zI-A5SM3oP22T-B*O8w^%m3{RJ)bjQG(p14KKO_c;;$PH9Z|9*R>l1!P^()Y*u9Bmq zj<} z4`3S&Z`ApjXiYQIwv`sju3V@}5%Ifp{bu|Ggf!zEX@)Zoy$_~ag@zZ}26c|Ir^)u;pS^6R zNv{smj;!ad4|8}gn>;L_Yp}+<4jP~IbWo4+Xu;u=b3_h@37kwP%IuZeS9up$Q9&t+ zMdeyJv5%~ZAxjQR)+1Z#(diW(X$3C&)CXv0u0bDKhrOLExl(UyV7Zs)>uWYTYyM3f z4dnv>T$hOx?$K-v0HOo?1L72x_|p6(l!HIYprDFvll7j}s#`fzK00!~0>9FVJwk4{&&8QJ?gB`ciJw%}Q5H0Nl|LTF_5 zg&Z&Mt@%5IqTT=iV2!J-G9j3$7{W__Otofl0C0@pQ6}D&Xu{7F{Q&^ln7j>7cvtSf zxb!vBJi|@5;WRnee$k|kSNN;;9Y571{5r|BV_=vE)>yW-#F*PJr{27yZe!K={Yc#w zoKBJzc={gm;MQ}G*8VqE>A+i@R#gyfETiKYaNj7Lzx=2{B7jVi*hJ)ACvr8@(<7c)|-C8 z_5^XLw_6v)-Jphz{95kd(u7WMZ&#e-GKm<)@Hqp#5>=4I#CP%T;-y+?7iL=&s{8&K zXAhdRS0%^i5q}h8I^z}Zr4Dy$2~kKOhcKshvY^n~mahgK*~ z>|oqRgZcLSWWNniw0@e?c@H~KzZL*!CpLaqP}Fx-BvD%sxQR4i*WlVf=HYR*N_xA6 z^bx{OuJH7W4`DH|19PtzwZ(@b8^8_0K>l>-VOMyT%m^yRj-sUPRzB7T3B%K02IAB>_Tg0SxS#)v5ypGJMD#=i$0 z|NH|=c|*t-CnqHP-k8Kle~nRaM{BPG8+!p()z+r$L47zA`Tvd+3vxPLo27RSb~RSocUQ zz6IEkvx+Uojv@NKM(AMgiMbLjpcS7gT6z5WJ0x4!(3Af|!*YD_l1S>Wrr+Z(O`o#$ z-eeBfRxRJo{-(F!DIR1)4cFBMULoGPA;bIl2%KgpQ<5T|(}Yr{8jYOWv2nOTlxULI z@2~7%#cw)G;9iDK6N#r&K?|XA7$_02l(a|tKOhBdzB~6Rl^sV$4(|5m$GELds1SF1 zKaK0%K8Kl&S7NvUbhqI~0I8MdZy9-khBh9jq4ghvuO}1n1~Wo(=Saq0fXUYW-ktWC9bgys)^tuFCX;%V zGHR-F`|$d;FV{?g_7l2FX@BZn{Y&3E6^8*9qN0pMrDDlYp+++yin1SL-TjvtkSDI; z)4vm#)~%QK&%3z*Y>vkXp>F>-X7Mfq**N zJ+_tA&)k4C6R0`JOm+svPNd59bdDihg!iJBA?LUZ-yz0;FsHu>$3MynX>;ZplErVC(^E`xRlHOgG&>cCx)o& zwjfgMp6)lR+T|&R(aBWY5Ey~7nYZWp$STD0U76FxL`X_Keu$v_3;%9sDMN;rlqW>+ z>b@b)V+@1%Dn9Sk!8d{>EztT8zZrQl!0?CiDt~Id_S9$gnX86k>fEfV{8d4CKOyOC z*l>@)GZF`>&BHm{r(jn$kuWopf1b^dL`<=9eC~eKnTT4F^T=;{?b{15?{~h+&UVtD zEDZxXNm{L-0JsS4oc{n(^PDsALd&b;5s6pI0La@mUHHDkUbB{rf$h zU>a)=k?;CzYWO#^R*Q}CN{5kz7~*Vc^{C|Rf&pOnK~kuS&?$~E$O@1gnJaRfE|k?Y z{jqOy>vmfDIA7)rf$HRk1xMzDRLuJn9SwE)-(ah=c=>i@IS=pFz#ZC$1c`d?Ib}%= zdFm-AD^ucUYAqV?trwnswR*YZ)h_gPYhA5z;~uc z_@%mC1?O*e9V~SDeJMZ4HgXzqmLN0z)FG-Ee3eNr#K+4kRf5?He3q<10Z@Wrb({K% zJZQa~eCWduG*BH1ip-J2G+HqA_z;_HC6`RGz6F@^5RR~&zcVh(`j)yzhV>*(t&;eR zZ-6f3-Y)iHP{Kb4RCq|B`Va3ZUiyEm$qm?Vui%0*KY}r(H`<$YBjA2g@jFt~92&eO zt2o6mkG$cVPeN^VtbUjrW&W^=SQ;GDwcmjgRdm!gJZGXtX;XIuzN(SwEH+`oA1PRC1%3*M5AcM>1BOTE#Wk70LhDz z&N@2jhz8~OA#Aj3I`4rco#1X8;vp$`P-&g2C{JMK$3s4&hk$@@TiVf`$9%3*yFzQE zP`8;2O*JiUZ+|u44!SNq5p0A~aJF|3&i?xnCK=&6M)o~p%6Zdgjp}HXp4HgYYM*Pd zapQrg9GZzU1%Yvk`4SajyzkUgmkqz?`WD`BuPeQ>>#X%2XZo(e+yfxPeN{Y5zQ^ZU zny9na3W}Udv6YSWLxtjt6+CKfiQI(HC|W=u83-c}S;WN%NHVDd-cbYBPyz4_0mOa2DwmsxcveCMJI%(QB;%B=ktjxck$j{B}agaYf zd)+rsK95`To*XxRfP79lgAq8p^%A37_;6PNba@T}N#4!nD=>HH_TOR=$<}zNKYn6l z)9mb(--4(&!XNyhRtT`0fY5*JQj~@5uC|G5WcrCmzqJOmIsU6#Doy;1#BjdgAn!s# zdeytBZll9B+DPx=N_vHUJn5jFKwO0acY0ztQA($S$OoF&akyLjb7I+ts8W*o=tkfL&Jp1kdAQvJc&2qlv>TirGz}8(m67R28Da{nAsC zs=4wTa-P+3e@clV+?OOxA#R`zvti_c2NcG8%$R)H+bH~fJWFU8@YUcKE3=Zr(3-8z4T`_Z zUKIu8azyxX)7IYLsVr?wPQ(a=`h#M8H(C$+s^N7%>$Som57N8g?(RH=cgrg}r2)ypk^n!))H6nZztKle2`GY6rr@QHipuP`3SIh(IwwejfA3c&7BLj?BOKAK5=rNXJD#{W5oI z*N!ykZr#%?vl-l9c0Ek<@3t#tgf{SX)emMhfTZKnkt;^~oEFnCu=J-n#9SLpz8&Yt zD)}q#NwWP?PqsM34KvVcY`E!d&JOgD0c5p%5SfMwJh6jJsWUPl+yL8xHP}<*`cXuvLg-X6dhBP>QAuVKtn7T>!nCV9EOUi|t18(A zp$nD{wu@fC@p{?AU=7o?wU46wb;5`xkj>#2V&%(-d+#yYUG2V$~e10f!#X7|b3S8xQIS7`#zEpJ9i64PfRmXd}d zIl0jLUw$EebNcEcVJ1JWEru8g=ibq+Pe7Cckh35d}Pwu z=+wPCX@});35L-IIFz-c*^;E^eg5VbfKZYKQs;JqtJKP{k_RMexe~*qHF(8TBs8d` z3bt^6NvHWcZMoh=pZpeNMCkL-ui!Be0MLi(NEz;~ZU1tjlr=0y(zU7U8`Sz3ythLI zEw5^Ys&Vyo9aV1JzjEiSLz@Lx9^1>>;dGP?rXtUdLauR&Z6*_c=4q{I#Ao5%um#$a z@6)S_2cfOkz&(vk6hh_&AU6V`U72+!p>hCYbp5<`KKs?v9my5T*8>hi zv}H%7X7=d;(Bmyh`Qk|^s5$trtlrFnt<4Jw#(DhPU%91_FcY-d^i|#{2QLixMQGB< zg10RKb8siJ1zWgY7sv4S&LWjLM^Hy5gWUc)PDfnEzWIj_)x_+6R~_i#bpE-)R5!Beahlo}mh8^=RcHR{LJ z;+*PWk>>ki!cY#caWT)Wtd?eH+^_yAyNW>e>KnZF(g`tmhP(=>1=c%%;g$A z)c1Zw6=COB?1$Yc(tmm{ab(b4H42J8HEV5v*%$@wYLZ&^X{x+rWeXGRdaU7NDR=HV z1k;Q-SGZqFNd5J>PI7Gzq~9%XA}HEF@ymP!0z#SM&1_DsmH)2JWQT>#*zzYhyP+~A z6Vdgen<6)QF#uK5jqGJ}+0OSU!s@(p0I%nH6L}NB!od0X#V%2(87=MYp!emyd~6nU zUA}G9@udVaHPdfAikl-JVd)4&S>jDdfJK2`pPN(|I1nYSB@Tq_ zxv`_9$ADWJuESXyhy5o(q)V+W)|W8Bw>O-gBH)t$7(D{Ky4p>Y7c(;tZ>N|El26}3 z-n7#XFr$JVSufXoPCDvwm|$g}BygO@S}w*UF~{Jyc=wk2|= zog7ypxQ55ETD-W}3lv!5o{qk9pz!2{|D)hdf}ENFj=SB6tw=qgHUM?#pn(=OL+h1@ zr{_Wg{!W?kJ9o%Ct8D?h$im=n8LsG5Zg?h+AyXHY&>&z-WD#(8r_P5LYdk@XYZ0$U zl&%xdT zn4EJ0m0zxS!r_ZjH6$L^KSnf`F$c}xEGoldH;ezL)ZinF_0fbnW0C535BH#1lAubB z=WzHz@Kk|Fa$^ynfEYS`1$Xf0@(0Gd%bH7ZG9$7){VQ2dFQ*R`qK|CB8l?TOkW0EO}<3=(2)WM;2d*<}ESNh|kp&qKWb2@i$lBWNCjey42F#xX)rJ zSxT5JV_@2z;kMOC`kTf`4t=VjD%lLM3W+o~9WGo!onWo+!zx11c&^?tiJ4u@n7zvE z4&?MNS(C5L)%4_wDcby2ui1&w?YF4lD= zC*|46vW#o%PoUp|MaryU07Z6Z;8}_+E~+F=bCBP3X4U|_SI|4mWKC5ZfnY~U%lF1;|p;X zXE*Q)Yi&&iDb_Hg+cSErrtJ6Xe(LFXqITRXl(w7cXL3X9sP6B@QBVv<1uJX7-#Q?; znp=cxZ(QHy?uw9coLsjzNgKXBC*YS5;s`aw7@UbAx#D#A8VV@C05c;jN=3?nr?c3QWP#Z&E$cA^(;>-Nt`7TeM zPpQpG>mAi|OCH}gBjbR#ugJ@NB~;~sZFkc%1B*d8W~PrdV`w))wz#+~BgsOClB^V8 z9o3=zge^uK4vsFs&5M}p1}#4CcrU0_=MJF?=G_2q2AF-+^RBCXy=b$%4ea`Uwx%e2 z%Py*T`IyVUkVRs06r}=2d^?E1s%f?0YWpi~H6Rx{ym~@R1^cE#md2H=@3)S^BElgx z%UetDUsH?349H`OETQEi)$FZYe3sN^V{XY8{B2YoHC{rIhWT&RX8mz@{Sj@kYk7n7 zF#uzG7Is5-W zpI&hPh1Gwyeh0rf_c5zzAz{42b+s`Kc7m6Jff>auFNmST`j2>Ph)GJ?>=(mi`fqCn z!p;4fWG+yuIqR7Z$ugnjX@=thW*<>Vt(G5OqjC1HJF6jf=l5L9mWfVS$g)5v1@Z;Y)g z=?UM#qzl?iOch=xiGnzHknPh`FnElF>_XYQxUI|mg%`1_JMDGT> zhs&es)JjA}JrYEMN2L7;X;m(2=sq0EG#2ywnYh)rSKiOh4wy{E6S25?jC8Spj|rXI zMg?+a(q8d*y4jF1C6PLZful0I(MqlzPAXV#u$M?Xag9`@ z{|D#?10Iq28_#Fg!R%O`t#)MU-X*@AzK+A>cTmmpu=YusAE&Hj^GcK=;0(Q%+TNJV ztQk=ZXD7*9icpl{WjLn|A83dnCr#%{6O08sOw=y`&Q6M*}AS^IqlY~2URjP0&O%I7O_ z^zq3!L!xdz_on%PsB4A&v1##SSYZ$(gB*Dbe2n&UNj$KKYKacRzu0#8Y$pzXgM#Jm zsZk7h>7=dEgl$NZmlxhx#O3((6H2oiLWQA>@kpe=+)4|eQkkXu>VRM&&S>G!$bi1_ zP*F)Bg{H3g0Bh7PI;!|H&V!BAJ7usPx~OF5&-@$#;W*PY%xo}(hgQ@Mi+Xjo9NLih z_R_jvUM+fc1rsSKJu)Bd4hxhaE|?f3gY#xgOE!Tf>^P)leemrIf=T(sG+0P!_cwso zHA}dauZF31`w@9#FB+42QnMIw#72^QJ<^NaMHK*$=!v{7$dLWM$FoEpq0Wm`u!Et^ zIrq-)vB91=7DC~y0|4r14NI3Ibnti$&P$29)e#Q7reFpSwhZoPM6^`zmIA`{g3CZK z6-51Gr2=4zONiXz>YtQfPYxLbu-N>p+(?z((8)}`d3vwNrfMaPGP?`1v`ZLXX%vRM4_(wm?H+GNkm8ArhL7Rz zJ;&!B>+LP3Jr!lXnmlgSiv8rKb^V9|e1SI9;4Q_vOtF|-E)5}y6-S!kHK3d%-}S#X zdzcbD2Tse1i7b-Ac7(4|O30foe}d(VW<((^&KtDR#u|U#{av9nb8#ipdkxj;X*ZI^ zCZog=Zw~<#Do`Uca>d(+dD>JO#p>Wgs6xpKeLq0~B`Y-PsZnP(%f&Da#Y-!sh?oAq z@Jzz^2g*ddOC7K)JA$h6J@Dw_I-d<+XN?Tk4X>>KD1tPi;K@T6LV=z z)HgY$FzG}^y|C5W8B-SEnUY51Qe7}EPaa-bK+=~}b4WW*E1?E+8SJJyoTUN=1G#2) z4RZA0^-A)VPf#FJl8n!y@$u6?e-}3|C(VcW+@33YNTiC0sM*1~<&S@Ffcd+FPq>!k z3n&GuI&I0|7iYS*x=bh8IIkbOsQ`2~UP8Hl$D`5R2=l7h&lITGwY2-pIU_;~ZnOyd z<#FX&(U!U{!&f$cucl}5El=8v)Ol{G%c)(^D5$8IEVI8_Nx?AAj`A=ks`ofChY+to zCg8lnIkmCkqqNUS6{;DpnUptL?|L=l`$wyLFfNz>T+Vg!7W7Uu*uFEX2{uQlYOuw` zKQoxm-s+L>;RFCv|NTQAH8jc$dA1pXj= zY|)_bLtV||Q^xYN+mi!)^BI@5iKZW2OwZRH>rHsFuVo2Oqnsa|Jk+$@kfSB*!f@w@ zP6z0(S3~%4$i^#nrn51-KSE1hPFUfpiUN-max@im>N=B5hJt)o7Atl5cN8q_OVACe*$qd~#k){n3&G3PzmM}F(Y?`%4sfos+s27v zLF85-SU^k(aWhg4qsc6j)m+EXkxG6&9-xa&9B_zwH6ECd%8Gsqu}Q}=*4qC_J&w6m@EiU?+z3mTYdxKbal+wo4YrcD%Shlq+!7;8;;*?8VxXC*ct%pz<}^fa&=jW-P8;BfGK;-WVnyOepi zg^pYa?rKPWiaq@oj>%vF9dLqvFrHue#89C#Tp8n*+=}&?_Jpq|CR#)%ndq2G*f(=m z@|}W#5y!|9WvR&piEYLL3njj$|6oJPX4aGM$#29O>QSTa9lk|ZJ2Zg1t4UNUxomki zzwCU8+OFY{QnxtIb-17bDSfY`(q6E#$)+RAhuy3Z(AB`MP^T=V-rtY}-x<6`1!7Jn zc8F%8o-kCG%$eI4%P&?CUaKzd3q$Y6#S6Su)m4rAGsQ6!`+FTmcoo-l&|spCi9xgf z@Yq_uXEktS1dhWuI{VgLOqTZQ-hFetS0z}*Ph(C?UDU55%Ui{)5iWy#M&@7rT!Y!P z%2$6V_&zPlVmVPA!Xmcn>jzOFlAieK3O))))IFj^ez7_s%-S=Z4Z`R&H%1y4XL{02 zVwKQ3o+sNiS{RW@Cylv_3oh2Qti-}v3PwMtf_pP|qNhT~vM3GvJD2KhOEC#F(j z3NM<7UR5>SvLUE{O`tQ=Vnf0pLXxu`jvm)>gq`U^p?`amU_ewIeixEpZ=^fX&i=+4 z=qou;#uVm^3Jd5<@5W62z6Ke(E*muEwYRt55Vq8nvK12~R$yW#au}07lK93a`0- zT{i(OilHAqrC8ly5|>hJnHY|iDwqgy(LeITC)2P?(TG7@&;W+(%B0;eWrf_7d=Vka znY@02(=Z~_cR#45rufnbXQ~|;EtGB$lI8h59sa;c_(L+BdR+jI7d)j%C1#~0N-8Ys(5r6|z2&+iDY2S_VCGlN93&4aC@D=w z&|P9whk3+@ZsR38@xvmt0DsI3PU(;~a_pmqutI^>9c>j25rS2!4~5SvO9XX}v0!HG zbncXNJ4S(8EwVlfNkO}!@)1Aj@1|>@(b`O`e?Z6x8q_JY;VvsxEE4n=<<;Zk*s|wF zeI+z5wPIK+Ju#F^Xj3wXLB8>95ar1QfRYck7P-Y_9NgwGzJe`;6SeRsmM|Fz@JM+2 zxp6!;(%j(c^uI1dE^k0|!21Sh0kP)eZ&q4YDIHfc6IXLVQ)lxJ0^nrl(1BKK(xeb`EA%7GD2<0mgRu+z$bOtd!E18VTdz{{cpayD|U( literal 0 HcmV?d00001 diff --git a/examples/PWA-example/public/logo_512.png b/examples/PWA-example/public/logo_512.png new file mode 100644 index 0000000000000000000000000000000000000000..43129772a892a5ca82dfdc8bb4d9027cc830ddf5 GIT binary patch literal 48151 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 Unavailable', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html' + }) + }); + } + }) + ); +}); + +/* The activate event fires after a service worker has been successfully installed. + It is most useful when phasing out an older version of a service worker, as at + this point you know that the new worker was installed correctly. In this example, + we delete old caches that don't match the version in the worker we just finished + installing. +*/ +self.addEventListener("activate", function (event) { + /* Just like with the install event, event.waitUntil blocks activate on a promise. + Activation will fail unless the promise is fulfilled. + */ + //console.log('WORKER: activate event in progress.'); + + event.waitUntil( + caches + /* This method returns a promise which will resolve to an array of available + cache keys. + */ + .keys() + .then(function (keys) { + // We return a promise that settles when all outdated caches are deleted. + return Promise.all( + keys + .filter(function (key) { + // Filter by keys that don't start with the latest version prefix. + return !key.startsWith(version); + }) + .map(function (key) { + /* Return a promise that's fulfilled + when each outdated cache is deleted. + */ + return caches.delete(key); + }) + ); + }) + .then(function () { + //console.log('WORKER: activate completed.'); + }) + ); +}); diff --git a/examples/PWA-example/src/main.rs b/examples/PWA-example/src/main.rs new file mode 100644 index 000000000..4c09a9bc5 --- /dev/null +++ b/examples/PWA-example/src/main.rs @@ -0,0 +1,20 @@ +use dioxus::prelude::*; + +fn main() { + // init debug tool for WebAssembly + wasm_logger::init(wasm_logger::Config::default()); + console_error_panic_hook::set_once(); + + dioxus_web::launch(app); +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! ( + div { + style: "text-align: center;", + h1 { "🌗 Dioxus 🚀" } + h3 { "Frontend that scales." } + p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." } + } + )) +} From 60eb453aada6b65c4857b28e48cd18afe8cedab8 Mon Sep 17 00:00:00 2001 From: Ell Date: Mon, 24 Apr 2023 15:40:00 +0500 Subject: [PATCH 365/516] Made clone_repo error clear. --- src/cli/translate/mod.rs | 4 +--- src/tools.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 20ca724fd..3056a6c77 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -1,8 +1,6 @@ use std::process::exit; -use dioxus_rsx::{BodyNode, CallBody, Component}; -use proc_macro2::{Ident, Span}; -use syn::punctuated::Punctuated; +use dioxus_rsx::{BodyNode, CallBody}; use super::*; diff --git a/src/tools.rs b/src/tools.rs index 704104de6..37ecc4e38 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,6 +1,6 @@ use std::{ fs::{create_dir_all, File}, - io::{Read, Write}, + io::{Read, Write, ErrorKind}, path::{Path, PathBuf}, process::Command, }; @@ -46,7 +46,13 @@ pub fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> { let mut cmd = Command::new("git"); let cmd = cmd.current_dir(target_dir); - let _res = cmd.arg("clone").arg(url).arg(dir_name).output()?; + let res = cmd.arg("clone").arg(url).arg(dir_name).output(); + if let Err(err) = res { + if ErrorKind::NotFound == err.kind() { + log::error!("Git program not found. Hint: Install git or check $PATH."); + return Err(err.into()); + } + } Ok(()) } From 9f238fa464189483651a741c2331375d325d44cd Mon Sep 17 00:00:00 2001 From: Ell Date: Mon, 24 Apr 2023 20:34:17 +0500 Subject: [PATCH 366/516] Minor improvments. --- src/cli/plugin/mod.rs | 10 ++++++---- src/plugin/mod.rs | 10 ++++++---- src/tools.rs | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/cli/plugin/mod.rs b/src/cli/plugin/mod.rs index 9179f5bd6..ca2937306 100644 --- a/src/cli/plugin/mod.rs +++ b/src/cli/plugin/mod.rs @@ -21,10 +21,12 @@ impl Plugin { } } Plugin::AppPath {} => { - if let Some(v) = crate::plugin::PluginManager::init_plugin_dir().to_str() { - println!("{}", v); - } else { - log::error!("Plugin path get failed."); + if let Ok(plugin_dir) = crate::plugin::PluginManager::init_plugin_dir() { + if let Some(v) = plugin_dir.to_str() { + println!("{}", v); + } else { + log::error!("Plugin path get failed."); + } } } Plugin::Add { name: _ } => { diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index c75e96444..5648b395f 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -42,7 +42,7 @@ impl PluginManager { let manager = lua.create_table().unwrap(); let name_index = lua.create_table().unwrap(); - let plugin_dir = Self::init_plugin_dir(); + let plugin_dir = Self::init_plugin_dir().unwrap(); let api = lua.create_table().unwrap(); @@ -288,15 +288,17 @@ impl PluginManager { Ok(()) } - pub fn init_plugin_dir() -> PathBuf { + pub fn init_plugin_dir() -> anyhow::Result { let app_path = app_path(); let plugin_path = app_path.join("plugins"); if !plugin_path.is_dir() { log::info!("📖 Start to init plugin library ..."); let url = "https://github.com/DioxusLabs/cli-plugin-library"; - clone_repo(&plugin_path, url).unwrap(); + if let Err(err) = clone_repo(&plugin_path, url) { + log::error!("Failed to init plugin dir, error caused by {}. ", err); + } } - plugin_path + Ok(plugin_path) } pub fn plugin_list() -> Vec { diff --git a/src/tools.rs b/src/tools.rs index 37ecc4e38..f11befaf5 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -49,7 +49,7 @@ pub fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> { let res = cmd.arg("clone").arg(url).arg(dir_name).output(); if let Err(err) = res { if ErrorKind::NotFound == err.kind() { - log::error!("Git program not found. Hint: Install git or check $PATH."); + log::warn!("Git program not found. Hint: Install git or check $PATH."); return Err(err.into()); } } From 61dba2825cf9430c8ebd0e6bdaeaa6fe2c2a4d7d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 24 Apr 2023 11:16:53 -0500 Subject: [PATCH 367/516] Improve the custom hooks chapter --- docs/guide/Cargo.toml | 6 +- docs/guide/examples/hooks_anti_patterns.rs | 38 +++++++++++ docs/guide/examples/hooks_composed.rs | 54 +++++++++++++++ docs/guide/examples/hooks_custom_logic.rs | 57 ++++++++++++++++ .../src/en/interactivity/custom_hooks.md | 65 +++++++++++++++++++ 5 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 docs/guide/examples/hooks_anti_patterns.rs create mode 100644 docs/guide/examples/hooks_custom_logic.rs diff --git a/docs/guide/Cargo.toml b/docs/guide/Cargo.toml index 227bdfa35..3919dc11c 100644 --- a/docs/guide/Cargo.toml +++ b/docs/guide/Cargo.toml @@ -16,12 +16,12 @@ dioxus-native-core-macro = { path = "../../packages/native-core-macro" } dioxus-router = { path = "../../packages/router" } dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] } dioxus-tui = { path = "../../packages/dioxus-tui" } -fermi = { path = "../../packages/fermi" } -shipyard = "0.6.2" - # dioxus = { path = "../../packages/dioxus", features = ["desktop", "web", "ssr", "router", "fermi", "tui"] } +fermi = { path = "../../packages/fermi" } +shipyard = "0.6.2" serde = { version = "1.0.138", features=["derive"] } reqwest = { version = "0.11.11", features = ["json"] } tokio = { version = "1.19.2" , features=[]} axum = { version = "0.6.1", features = ["ws"] } +gloo-storage = "0.2.2" diff --git a/docs/guide/examples/hooks_anti_patterns.rs b/docs/guide/examples/hooks_anti_patterns.rs new file mode 100644 index 000000000..2119b8601 --- /dev/null +++ b/docs/guide/examples/hooks_anti_patterns.rs @@ -0,0 +1,38 @@ +#![allow(unused)] + +use dioxus::prelude::*; + +fn main() {} + +// ANCHOR: non_clone_state +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Arc; + +struct UseState<'a, T> { + value: &'a RefCell, + update: Arc, +} + +fn my_use_state(cx: &ScopeState, init: impl FnOnce() -> T) -> UseState { + // The update function will trigger a re-render in the component cx is attached to + let update = cx.schedule_update(); + // Create the initial state + let value = cx.use_hook(|| RefCell::new(init())); + + UseState { value, update } +} + +impl UseState<'_, T> { + fn get(&self) -> T { + self.value.borrow().clone() + } + + fn set(&self, value: T) { + // Update the state + *self.value.borrow_mut() = value; + // Trigger a re-render on the component the state is from + (self.update)(); + } +} +// ANCHOR_END: non_clone_state diff --git a/docs/guide/examples/hooks_composed.rs b/docs/guide/examples/hooks_composed.rs index 801474aa3..782a19d7d 100644 --- a/docs/guide/examples/hooks_composed.rs +++ b/docs/guide/examples/hooks_composed.rs @@ -11,3 +11,57 @@ fn use_settings(cx: &ScopeState) -> &UseSharedState { use_shared_state::(cx).expect("App settings not provided") } // ANCHOR_END: wrap_context + +// ANCHOR: use_storage +use gloo_storage::{LocalStorage, Storage}; +use serde::{de::DeserializeOwned, Serialize}; + +/// A persistent storage hook that can be used to store data across application reloads. +#[allow(clippy::needless_return)] +pub fn use_persistent( + cx: &ScopeState, + // A unique key for the storage entry + key: impl ToString, + // A function that returns the initial value if the storage entry is empty + init: impl FnOnce() -> T, +) -> &UsePersistent { + // Use the use_ref hook to create a mutable state for the storage entry + let state = use_ref(cx, move || { + // This closure will run when the hook is created + let key = key.to_string(); + let value = LocalStorage::get(key.as_str()).ok().unwrap_or_else(init); + StorageEntry { key, value } + }); + + // Wrap the state in a new struct with a custom API + // Note: We use use_hook here so that this hook is easier to use in closures in the rsx. Any values with the same lifetime as the ScopeState can be used in the closure without cloning. + cx.use_hook(|| UsePersistent { + inner: state.clone(), + }) +} + +struct StorageEntry { + key: String, + value: T, +} + +/// Storage that persists across application reloads +pub struct UsePersistent { + inner: UseRef>, +} + +impl UsePersistent { + /// Returns a reference to the value + pub fn get(&self) -> T { + self.inner.read().value.clone() + } + + /// Sets the value + pub fn set(&self, value: T) { + let mut inner = self.inner.write(); + // Write the new value to local storage + LocalStorage::set(inner.key.as_str(), &value); + inner.value = value; + } +} +// ANCHOR_END: use_storage diff --git a/docs/guide/examples/hooks_custom_logic.rs b/docs/guide/examples/hooks_custom_logic.rs new file mode 100644 index 000000000..2323ddb11 --- /dev/null +++ b/docs/guide/examples/hooks_custom_logic.rs @@ -0,0 +1,57 @@ +#![allow(unused)] + +use dioxus::prelude::*; + +fn main() {} + +// ANCHOR: use_state +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Arc; + +#[derive(Clone)] +struct UseState { + value: Rc>, + update: Arc, +} + +fn my_use_state(cx: &ScopeState, init: impl FnOnce() -> T) -> &UseState { + cx.use_hook(|| { + // The update function will trigger a re-render in the component cx is attached to + let update = cx.schedule_update(); + // Create the initial state + let value = Rc::new(RefCell::new(init())); + + UseState { value, update } + }) +} + +impl UseState { + fn get(&self) -> T { + self.value.borrow().clone() + } + + fn set(&self, value: T) { + // Update the state + *self.value.borrow_mut() = value; + // Trigger a re-render on the component the state is from + (self.update)(); + } +} +// ANCHOR_END: use_state + +// ANCHOR: use_context +pub fn use_context(cx: &ScopeState) -> Option<&T> { + cx.use_hook(|| cx.consume_context::()).as_ref() +} + +pub fn use_context_provider(cx: &ScopeState, f: impl FnOnce() -> T) -> &T { + cx.use_hook(|| { + let val = f(); + // Provide the context state to the scope + cx.provide_context(val.clone()); + val + }) +} + +// ANCHOR_END: use_context diff --git a/docs/guide/src/en/interactivity/custom_hooks.md b/docs/guide/src/en/interactivity/custom_hooks.md index 07472bb95..f3acec269 100644 --- a/docs/guide/src/en/interactivity/custom_hooks.md +++ b/docs/guide/src/en/interactivity/custom_hooks.md @@ -2,6 +2,8 @@ Hooks are a great way to encapsulate business logic. If none of the existing hooks work for your problem, you can write your own. +When writing your hook, you can make a function that accepts `cx: &ScopeState` as a parameter to accept a scope with any Props. + ## Composing Hooks To avoid repetition, you can encapsulate business logic based on existing hooks to create a new hook. @@ -12,6 +14,12 @@ For example, if many components need to access an `AppSettings` struct, you can {{#include ../../../examples/hooks_composed.rs:wrap_context}} ``` +Or if you want to wrap a hook that persists reloads with the storage API, you can build on top of the use_ref hook to work with mutable state: + +```rust +{{#include ../../../examples/hooks_composed.rs:use_storage}} +``` + ## Custom Hook Logic You can use [`cx.use_hook`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.use_hook) to build your own hooks. In fact, this is what all the standard hooks are built on! @@ -23,4 +31,61 @@ You can use [`cx.use_hook`](https://docs.rs/dioxus/latest/dioxus/prelude/struct. Inside the initialization closure, you will typically make calls to other `cx` methods. For example: - The `use_state` hook tracks state in the hook value, and uses [`cx.schedule_update`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.schedule_update) to make Dioxus re-render the component whenever it changes. + +Here is a simplified implementation of the `use_state` hook: + +```rust +{{#include ../../../examples/hooks_custom_logic.rs:use_state}} +``` + - The `use_context` hook calls [`cx.consume_context`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.ScopeState.html#method.consume_context) (which would be expensive to call on every render) to get some context from the scope + +Here is an implementation of the `use_context` and `use_context_provider` hooks: + +```rust +{{#include ../../../examples/hooks_custom_logic.rs:use_context}} +``` + +## Hook Anti-Patterns + +When writing a custom hook, you should avoid the following anti-patterns: + +- !Clone Hooks: To allow hooks to be used within async blocks, the hooks must be Clone. To make a hook clone, you can wrap data in Rc or Arc and avoid lifetimes in hooks. + +This version of use_state may seem more efficient, but it is not cloneable: + +```rust +{{#include ../../../examples/hooks_anti_patterns.rs:non_clone_state}} +``` + +If we try to use this hook in an async block, we will get a compile error: + +```rust +fn FutureComponent(cx: &ScopeState) -> Element { + let my_state = my_use_state(cx, || 0); + cx.spawn({ + to_owned![my_state]; + async move { + my_state.set(1); + } + }); + + todo!() +} +``` + +But with the original version, we can use it in an async block: + +```rust +fn FutureComponent(cx: &ScopeState) -> Element { + let my_state = use_state(cx, || 0); + cx.spawn({ + to_owned![my_state]; + async move { + my_state.set(1); + } + }); + + todo!() +} +``` From bba7eaa61eea0ca22b1284431b062182403b313c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Z=C3=B6chbauer?= Date: Mon, 24 Apr 2023 18:22:32 +0200 Subject: [PATCH 368/516] Fix typo --- docs/src/cmd/serve.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/cmd/serve.md b/docs/src/cmd/serve.md index e5c10a902..4a699267b 100644 --- a/docs/src/cmd/serve.md +++ b/docs/src/cmd/serve.md @@ -45,5 +45,5 @@ You can use `--example {name}` to start a example code. ``` # build `example/test` code -dioxus serve --exmaple test -``` \ No newline at end of file +dioxus serve --example test +``` From 7be8ec3467c497985d0b334536dcea61685f635c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 24 Apr 2023 16:07:50 -0500 Subject: [PATCH 369/516] Fix html to rsx conversions --- packages/autofmt/src/element.rs | 13 +++--- packages/rsx-rosetta/Cargo.toml | 3 ++ packages/rsx-rosetta/tests/simple.rs | 68 ++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 packages/rsx-rosetta/tests/simple.rs diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index 6d2cdc830..5bb7d42fc 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -276,13 +276,12 @@ impl Writer<'_> { let start = location.start(); let line_start = start.line - 1; - let this_line = self.src[line_start]; - - let beginning = if this_line.len() > start.column { - this_line[..start.column].trim() - } else { - "" - }; + let beginning = self + .src + .get(line_start) + .filter(|this_line| this_line.len() > start.column) + .map(|this_line| this_line[..start.column].trim()) + .unwrap_or_default(); beginning.is_empty() } diff --git a/packages/rsx-rosetta/Cargo.toml b/packages/rsx-rosetta/Cargo.toml index d160d2bf9..e6bc45225 100644 --- a/packages/rsx-rosetta/Cargo.toml +++ b/packages/rsx-rosetta/Cargo.toml @@ -25,3 +25,6 @@ convert_case = "0.5.0" # default = ["html"] # eventually more output options + +[dev-dependencies] +pretty_assertions = "1.2.1" \ No newline at end of file diff --git a/packages/rsx-rosetta/tests/simple.rs b/packages/rsx-rosetta/tests/simple.rs new file mode 100644 index 000000000..b8fd095e1 --- /dev/null +++ b/packages/rsx-rosetta/tests/simple.rs @@ -0,0 +1,68 @@ +use html_parser::Dom; + +#[test] +fn simple_elements() { + let html = r#" +
+
hello world!
+
hello world!
+
hello world!
+
hello world!
+
hello world!
+
hello world!
+ hello world! +
+ "# + .trim(); + + let dom = Dom::parse(html).unwrap(); + + let body = rsx_rosetta::rsx_from_html(&dom); + + let out = dioxus_autofmt::write_block_out(body).unwrap(); + + let expected = r#" + div { + div { class: "asd", "hello world!" } + div { id: "asd", "hello world!" } + div { id: "asd", "hello world!" } + div { r#for: "asd", "hello world!" } + div { r#async: "asd", "hello world!" } + div { large_thing: "asd", "hello world!" } + ai_is_awesome { "hello world!" } + }"#; + pretty_assertions::assert_eq!(&out, &expected); +} + +#[test] +fn deeply_nested() { + let html = r#" +
+
+
+
+
+
+
+
+
+
+ "# + .trim(); + + let dom = Dom::parse(html).unwrap(); + + let body = rsx_rosetta::rsx_from_html(&dom); + + let out = dioxus_autofmt::write_block_out(body).unwrap(); + + let expected = r#" + div { + div { class: "asd", + div { class: "asd", + div { class: "asd", div { class: "asd" } } + } + } + }"#; + pretty_assertions::assert_eq!(&out, &expected); +} From 8af3522a2e3f5094aedc9714521a1cd848530d9c Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Mon, 24 Apr 2023 14:13:54 -0700 Subject: [PATCH 370/516] fix: change crossterm poll timeout to 10ms from 1s (#983) Problem: Key events were delayed signifantly in TUI widgets. Solution: Decrease crossterm event poll timeout from 1000ms to 10ms. Fixes #978 --- packages/rink/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rink/src/lib.rs b/packages/rink/src/lib.rs index 9fceb6b48..7f15aa751 100644 --- a/packages/rink/src/lib.rs +++ b/packages/rink/src/lib.rs @@ -98,7 +98,8 @@ pub fn render( let event_tx_clone = event_tx.clone(); if !cfg.headless { std::thread::spawn(move || { - let tick_rate = Duration::from_millis(1000); + // Timeout after 10ms when waiting for events + let tick_rate = Duration::from_millis(10); loop { if crossterm::event::poll(tick_rate).unwrap() { let evt = crossterm::event::read().unwrap(); From b0a1f4773e93b902f2429527a15f78e946b30938 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 25 Apr 2023 08:16:11 -0500 Subject: [PATCH 371/516] create file upload example --- examples/file_upload.rs | 37 +++++++++++++++++++++++++++++++++++++ packages/desktop/Cargo.toml | 3 ++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 examples/file_upload.rs diff --git a/examples/file_upload.rs b/examples/file_upload.rs new file mode 100644 index 000000000..c3771f7b0 --- /dev/null +++ b/examples/file_upload.rs @@ -0,0 +1,37 @@ +#![allow(non_snake_case)] +use dioxus::prelude::*; + +fn main() { + dioxus_desktop::launch(App); +} + +fn App(cx: Scope) -> Element { + let files_uploaded: &UseRef> = use_ref(cx, Vec::new); + + cx.render(rsx! { + input { + r#type: "file", + accept: "text/*", + onchange: |evt| { + to_owned![files_uploaded]; + async move { + println!("files uploaded: {:?}", evt); + if let Some(file_engine) = &evt.files { + let files = file_engine.files(); + for file_name in &files { + if let Some(file) = file_engine.read_file_to_string(&file_name).await{ + files_uploaded.write().push(file); + } + } + } + } + }, + } + + ul { + for file in files_uploaded.read().iter() { + li { "{file}" } + } + } + }) +} diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 5722a42a2..beb971ca9 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -23,12 +23,13 @@ thiserror = "1.0.30" log = "0.4.14" wry = { version = "0.27.2" } futures-channel = "0.3.21" -tokio = { version = "1.16.1", features = [ +tokio = { version = "1.27", features = [ "sync", "rt-multi-thread", "rt", "time", "macros", + "fs", ], optional = true, default-features = false } webbrowser = "0.8.0" infer = "0.11.0" From c1e2592299e0563cd0ef66626a44489c7ac6f0c4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 25 Apr 2023 09:26:56 -0500 Subject: [PATCH 372/516] implement file events on desktop --- examples/file_upload.rs | 4 +- packages/desktop/Cargo.toml | 3 +- packages/desktop/src/file_upload.rs | 74 +++++++++++++++++++++++++++++ packages/desktop/src/lib.rs | 28 ++++++++++- packages/desktop/src/protocol.rs | 28 +++++++---- 5 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 packages/desktop/src/file_upload.rs diff --git a/examples/file_upload.rs b/examples/file_upload.rs index c3771f7b0..c43e6e6ef 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -11,11 +11,11 @@ fn App(cx: Scope) -> Element { cx.render(rsx! { input { r#type: "file", - accept: "text/*", + accept: ".txt", + multiple: true, onchange: |evt| { to_owned![files_uploaded]; async move { - println!("files uploaded: {:?}", evt); if let Some(file_engine) = &evt.files { let files = file_engine.files(); for file_name in &files { diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index beb971ca9..8b41019e1 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] } -dioxus-html = { path = "../html", features = ["serialize"], version = "^0.3.0" } +dioxus-html = { path = "../html", features = ["serialize", "native-bind"], version = "^0.3.0" } dioxus-interpreter-js = { path = "../interpreter", version = "^0.3.0" } dioxus-hot-reload = { path = "../hot-reload", optional = true } @@ -37,6 +37,7 @@ dunce = "1.0.2" slab = "0.4" futures-util = "0.3.25" +rfd = "0.11.3" [target.'cfg(target_os = "ios")'.dependencies] objc = "0.2.7" diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs new file mode 100644 index 000000000..ae4a81cc2 --- /dev/null +++ b/packages/desktop/src/file_upload.rs @@ -0,0 +1,74 @@ +use std::{path::PathBuf, str::FromStr}; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub(crate) struct FileDiologRequest { + accept: String, + multiple: bool, + pub event: String, + pub target: usize, + pub bubbles: bool, +} + +pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec { + let mut dialog = rfd::FileDialog::new(); + + let filters: Vec<_> = request + .accept + .split(',') + .filter_map(|s| Filters::from_str(s).ok()) + .collect(); + + let file_extensions: Vec<_> = filters + .iter() + .flat_map(|f| f.as_extensions().into_iter()) + .collect(); + + dialog = dialog.add_filter("name", file_extensions.as_slice()); + + let files: Vec<_> = if request.multiple { + dialog.pick_files().into_iter().flatten().collect() + } else { + dialog.pick_file().into_iter().collect() + }; + + files +} + +enum Filters { + Extension(String), + Mime(String), + Audio, + Video, + Image, +} + +impl Filters { + fn as_extensions(&self) -> Vec<&str> { + match self { + Filters::Extension(extension) => vec![extension.as_str()], + Filters::Mime(_) => vec![], + Filters::Audio => vec!["mp3", "wav", "ogg"], + Filters::Video => vec!["mp4", "webm"], + Filters::Image => vec!["png", "jpg", "jpeg", "gif", "webp"], + } + } +} + +impl FromStr for Filters { + type Err = String; + + fn from_str(s: &str) -> Result { + if let Some(extension) = s.strip_prefix('.') { + Ok(Filters::Extension(extension.to_string())) + } else { + match s { + "audio/*" => Ok(Filters::Audio), + "video/*" => Ok(Filters::Video), + "image/*" => Ok(Filters::Image), + _ => Ok(Filters::Mime(s.to_string())), + } + } + } +} diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index cdb592f72..4d904e948 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -8,6 +8,7 @@ mod desktop_context; mod escape; mod eval; mod events; +mod file_upload; mod protocol; mod shortcut; mod waker; @@ -19,14 +20,14 @@ pub use desktop_context::{ }; use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers}; use dioxus_core::*; -use dioxus_html::HtmlEvent; +use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent}; pub use eval::{use_eval, EvalResult}; use futures_util::{pin_mut, FutureExt}; use shortcut::ShortcutRegistry; pub use shortcut::{use_global_shortcut, ShortcutHandle, ShortcutId, ShortcutRegistryError}; -use std::collections::HashMap; use std::rc::Rc; use std::task::Waker; +use std::{collections::HashMap, sync::Arc}; pub use tao::dpi::{LogicalSize, PhysicalSize}; use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget}; pub use tao::window::WindowBuilder; @@ -264,6 +265,29 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) } } + EventData::Ipc(msg) if msg.method() == "file_diolog" => { + if let Ok(file_diolog) = + serde_json::from_value::(msg.params()) + { + let id = ElementId(file_diolog.target); + let event_name = &file_diolog.event; + let event_bubbles = file_diolog.bubbles; + let files = file_upload::get_file_event(&file_diolog); + let data = FormData { + value: Default::default(), + values: Default::default(), + files: Some(Arc::new(NativeFileEngine::new(files))), + }; + + let view = webviews.get_mut(&event.1).unwrap(); + + view.dom + .handle_event(event_name, Rc::new(data), id, event_bubbles); + + send_edits(view.dom.render_immediate(), &view.webview); + } + } + _ => {} }, Event::GlobalShortcutEvent(id) => shortcut_manager.call_handlers(id), diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 3d4246987..5b0cd1cc7 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -14,15 +14,25 @@ fn module_loader(root_name: &str) -> String { r#"// Prevent file inputs from opening the file dialog on click let inputs = document.querySelectorAll("input"); for (let input of inputs) { - input.addEventListener("click", (event) => { - let target = event.target; - // prevent file inputs from opening the file dialog on click - const type = target.getAttribute("type"); - if (type === "file") { - window.ipc.postMessage(serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple") })); - event.preventDefault(); - } - }); + if (!input.getAttribute("data-dioxus-file-listener")) { + input.setAttribute("data-dioxus-file-listener", true); + input.addEventListener("click", (event) => { + let target = event.target; + // prevent file inputs from opening the file dialog on click + const type = target.getAttribute("type"); + if (type === "file") { + let target_id = find_real_id(target); + if (target_id !== null) { + const event_name = "change"; + const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); + console.log(message); + console.log(event); + window.ipc.postMessage(message); + } + event.preventDefault(); + } + }); + } }"#, ); format!( From 9cfb655478d3f12d18faaf9d955d8ca4739cf4b0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 25 Apr 2023 14:26:12 -0500 Subject: [PATCH 373/516] implement the file engine for liveview --- packages/desktop/src/protocol.rs | 11 +++--- packages/html/src/events/form.rs | 47 +++++++++++++++++++++++- packages/interpreter/src/interpreter.js | 21 +++++++---- packages/liveview/Cargo.toml | 1 + packages/liveview/src/lib.rs | 49 ++++++++++++++++++++++++- 5 files changed, 113 insertions(+), 16 deletions(-) diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 5b0cd1cc7..f6725c428 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -23,11 +23,12 @@ fn module_loader(root_name: &str) -> String { if (type === "file") { let target_id = find_real_id(target); if (target_id !== null) { - const event_name = "change"; - const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); - console.log(message); - console.log(event); - window.ipc.postMessage(message); + const send = (event_name) => { + const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); + window.ipc.postMessage(message); + }; + send("change"); + send("input"); } event.preventDefault(); } diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 6edf60448..ec65ffe03 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -12,10 +12,55 @@ pub struct FormData { pub values: HashMap>, - #[cfg_attr(feature = "serialize", serde(skip))] + #[cfg_attr( + feature = "serialize", + serde(skip_serializing, deserialize_with = "deserialize_file_engine") + )] pub files: Option>, } +#[cfg(feature = "serialize")] +#[derive(serde::Serialize, serde::Deserialize)] +struct SerializedFileEngine { + files: HashMap>, +} + +#[cfg(feature = "serialize")] +#[async_trait::async_trait(?Send)] +impl FileEngine for SerializedFileEngine { + fn files(&self) -> Vec { + self.files.keys().cloned().collect() + } + + async fn read_file(&self, file: &str) -> Option> { + self.files.get(file).cloned() + } + + async fn read_file_to_string(&self, file: &str) -> Option { + self.read_file(file) + .await + .map(|bytes| String::from_utf8_lossy(&bytes).to_string()) + } +} + +#[cfg(feature = "serialize")] +fn deserialize_file_engine<'de, D>( + deserializer: D, +) -> Result>, D::Error> +where + D: serde::Deserializer<'de>, +{ + use serde::Deserialize; + + let Ok(file_engine) = + SerializedFileEngine::deserialize(deserializer) else{ + return Ok(None); + }; + + let file_engine = std::sync::Arc::new(file_engine); + Ok(Some(file_engine)) +} + impl PartialEq for FormData { fn eq(&self, other: &Self) -> bool { self.value == other.value && self.values == other.values diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 162d5c8f5..81639b778 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -344,17 +344,19 @@ class Interpreter { this.RemoveEventListener(edit.id, edit.name); break; case "NewEventListener": - let bubbles = event_bubbles(edit.name); + const bubbles = event_bubbles(edit.name); - this.NewEventListener(edit.name, edit.id, bubbles, handler); + this.NewEventListener(edit.name, edit.id, bubbles, (event) => + handler(event, edit.name, bubbles) + ); break; } } } -// this handler is only provided on desktop implementations since this +// this handler is only provided on the desktop and liveview implementations since this // method is not used by the web implementation -function handler(event) { +function handler(event, name, bubbles) { let target = event.target; if (target != null) { let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`); @@ -387,8 +389,6 @@ function handler(event) { shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`); - let contents = serialize_event(event); - if (shouldPreventDefault === `on${event.type}`) { event.preventDefault(); } @@ -397,6 +397,10 @@ function handler(event) { event.preventDefault(); } + let contents = serialize_event(event); + + /*POST_EVENT_SERIALIZATION*/ + if ( target.tagName === "FORM" && (event.type === "submit" || event.type === "input") @@ -408,7 +412,8 @@ function handler(event) { const formData = new FormData(target); for (let name of formData.keys()) { - contents.values[name] = formData.getAll(name); + let value = formData.getAll(name); + contents.values[name] = value; } } } @@ -418,7 +423,7 @@ function handler(event) { } window.ipc.postMessage( serializeIpcMessage("user_event", { - name: edit.name, + name: name, element: parseInt(realId), data: contents, bubbles, diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 5269927b0..61fbe0887 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -36,6 +36,7 @@ axum = { version = "0.6.1", optional = true, features = ["ws"] } # salvo salvo = { version = "0.37.7", optional = true, features = ["ws"] } +once_cell = "1.17.1" # actix is ... complicated? # actix-files = { version = "0.6.2", optional = true } diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index ffddf0272..bea21a71b 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -34,7 +34,51 @@ pub enum LiveViewError { SendingFailed, } -use dioxus_interpreter_js::INTERPRETER_JS; +use once_cell::sync::Lazy; + +static INTERPRETER_JS: Lazy = Lazy::new(|| { + let interpreter = dioxus_interpreter_js::INTERPRETER_JS; + let serialize_file_uploads = r#"if ( + target.tagName === "INPUT" && + (event.type === "change" || event.type === "input") + ) { + const type = target.getAttribute("type"); + if (type === "file") { + async function read_files() { + const files = target.files; + const file_contents = {}; + + for (let i = 0; i < files.length; i++) { + const file = files[i]; + + file_contents[file.name] = Array.from( + new Uint8Array(await file.arrayBuffer()) + ); + } + let file_engine = { + files: file_contents, + }; + contents.files = file_engine; + + if (realId === null) { + return; + } + const message = serializeIpcMessage("user_event", { + name: name, + element: parseInt(realId), + data: contents, + bubbles, + }); + window.ipc.postMessage(message); + } + read_files(); + return; + } + }"#; + + interpreter.replace("/*POST_EVENT_SERIALIZATION*/", serialize_file_uploads) +}); + static MAIN_JS: &str = include_str!("./main.js"); /// This script that gets injected into your app connects this page to the websocket endpoint @@ -42,11 +86,12 @@ static MAIN_JS: &str = include_str!("./main.js"); /// Once the endpoint is connected, it will send the initial state of the app, and then start /// processing user events and returning edits to the liveview instance pub fn interpreter_glue(url: &str) -> String { + let js = &*INTERPRETER_JS; format!( r#" From d749fff11b06718215f8d1bb77b06799c7a23d1f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 25 Apr 2023 14:29:35 -0500 Subject: [PATCH 374/516] fix double diologs on desktop renderer --- packages/desktop/src/lib.rs | 13 +++++++++---- packages/desktop/src/protocol.rs | 3 +-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 4d904e948..807ebf48e 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -273,16 +273,21 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) let event_name = &file_diolog.event; let event_bubbles = file_diolog.bubbles; let files = file_upload::get_file_event(&file_diolog); - let data = FormData { + let data = Rc::new(FormData { value: Default::default(), values: Default::default(), files: Some(Arc::new(NativeFileEngine::new(files))), - }; + }); let view = webviews.get_mut(&event.1).unwrap(); - view.dom - .handle_event(event_name, Rc::new(data), id, event_bubbles); + if event_name == "change&input" { + view.dom + .handle_event("input", data.clone(), id, event_bubbles); + view.dom.handle_event("change", data, id, event_bubbles); + } else { + view.dom.handle_event(event_name, data, id, event_bubbles); + } send_edits(view.dom.render_immediate(), &view.webview); } diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index f6725c428..e35d67bb1 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -27,8 +27,7 @@ fn module_loader(root_name: &str) -> String { const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); window.ipc.postMessage(message); }; - send("change"); - send("input"); + send("change&input"); } event.preventDefault(); } From 2f8d6fb49f9240b4a084d7ce381cba7a98998473 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 25 Apr 2023 15:07:22 -0500 Subject: [PATCH 375/516] fix clippy --- examples/file_upload.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/file_upload.rs b/examples/file_upload.rs index c43e6e6ef..700c5392e 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -19,7 +19,7 @@ fn App(cx: Scope) -> Element { if let Some(file_engine) = &evt.files { let files = file_engine.files(); for file_name in &files { - if let Some(file) = file_engine.read_file_to_string(&file_name).await{ + if let Some(file) = file_engine.read_file_to_string(file_name).await{ files_uploaded.write().push(file); } } From 254947a1948239081b979fb740827b573f82e709 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 25 Apr 2023 15:08:53 -0500 Subject: [PATCH 376/516] fix liveview tokio features --- packages/liveview/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 5269927b0..2dbc09c71 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -18,7 +18,7 @@ futures-util = { version = "0.3.25", default-features = false, features = [ "sink", ] } futures-channel = { version = "0.3.25", features = ["sink"] } -tokio = { version = "1.22.0", features = ["time"] } +tokio = { version = "1.22.0", features = ["time", "macros"] } tokio-stream = { version = "0.1.11", features = ["net"] } tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } From cfe94950c62e27bdc8efc41573811386dda65ea7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 26 Apr 2023 18:05:13 -0500 Subject: [PATCH 377/516] make files optional when deserializing form data --- packages/html/src/events/form.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index ec65ffe03..113c765c5 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -14,7 +14,11 @@ pub struct FormData { #[cfg_attr( feature = "serialize", - serde(skip_serializing, deserialize_with = "deserialize_file_engine") + serde( + default, + skip_serializing, + deserialize_with = "deserialize_file_engine" + ) )] pub files: Option>, } From c9044111908338c347b2b00bb48f579e5d9e1877 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 27 Apr 2023 05:27:07 -0700 Subject: [PATCH 378/516] Fix typo in event_handlers.md (#991) --- docs/guide/src/en/interactivity/event_handlers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index 5e12ce259..bee0175e2 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -31,7 +31,7 @@ Some events will trigger first on the element the event originated at upward. Fo > For more information about event propigation see [the mdn docs on event bubling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling) -If you want to prevent this behavior, you can call `stop_propogation()` on the event: +If you want to prevent this behavior, you can call `stop_propagation()` on the event: ```rust {{#include ../../../examples/event_nested.rs:rsx}} From c645e1c6aaa44829b38f273d97f5ae274a5671f5 Mon Sep 17 00:00:00 2001 From: Attilio <75045331+attilio-oliva@users.noreply.github.com> Date: Thu, 27 Apr 2023 18:26:04 +0200 Subject: [PATCH 379/516] Allow multiple prevent default for Web, Desktop and LiveView (#987) * Allow multiple prevent default in dioxus_web * Use SplitWhitespace for prevent default requests * Add multiple prevent default option to liveview and desktop * Update docs about prevent_default attribute * cargo clippy * update prevent default guide example --------- Co-authored-by: Evan Almloff --- docs/guide/examples/event_prevent_default.rs | 3 +-- .../src/en/interactivity/event_handlers.md | 2 +- packages/interpreter/src/interpreter.js | 18 +++++++++++------- packages/web/src/dom.rs | 12 ++++++++---- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/guide/examples/event_prevent_default.rs b/docs/guide/examples/event_prevent_default.rs index b109700bf..32443d017 100644 --- a/docs/guide/examples/event_prevent_default.rs +++ b/docs/guide/examples/event_prevent_default.rs @@ -10,8 +10,7 @@ fn App(cx: Scope) -> Element { // ANCHOR: prevent_default cx.render(rsx! { input { - prevent_default: "oninput", - prevent_default: "onclick", + prevent_default: "oninput onclick", } }) // ANCHOR_END: prevent_default diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index bee0175e2..317038923 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -41,7 +41,7 @@ If you want to prevent this behavior, you can call `stop_propagation()` on the e Some events have a default behavior. For keyboard events, this might be entering the typed character. For mouse events, this might be selecting some text. -In some instances, might want to avoid this default behavior. For this, you can add the `prevent_default` attribute with the name of the handler whose default behavior you want to stop. This attribute is special: you can attach it multiple times for multiple attributes: +In some instances, might want to avoid this default behavior. For this, you can add the `prevent_default` attribute with the name of the handler whose default behavior you want to stop. This attribute can be used for multiple handlers using their name separated by spaces: ```rust {{#include ../../../examples/event_prevent_default.rs:prevent_default}} diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 9b93ec9e3..d7b74a63e 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -352,7 +352,7 @@ class Interpreter { let target = event.target; if (target != null) { let realId = target.getAttribute(`data-dioxus-id`); - let shouldPreventDefault = target.getAttribute( + let preventDefaultRequests = target.getAttribute( `dioxus-prevent-default` ); @@ -361,10 +361,14 @@ class Interpreter { let a_element = target.closest("a"); if (a_element != null) { event.preventDefault(); - if ( - shouldPreventDefault !== `onclick` && - a_element.getAttribute(`dioxus-prevent-default`) !== `onclick` - ) { + + let elementShouldPreventDefault = + preventDefaultRequests.includes(`onclick`); + let linkShouldPreventDefault = a_element + .getAttribute(`dioxus-prevent-default`) + .includes(`onclick`); + + if (!elementShouldPreventDefault && !linkShouldPreventDefault) { const href = a_element.getAttribute("href"); if (href !== "" && href !== null && href !== undefined) { window.ipc.postMessage( @@ -390,13 +394,13 @@ class Interpreter { realId = target.getAttribute(`data-dioxus-id`); } - shouldPreventDefault = target.getAttribute( + preventDefaultRequests = target.getAttribute( `dioxus-prevent-default` ); let contents = serialize_event(event); - if (shouldPreventDefault === `on${event.type}`) { + if (preventDefaultRequests.includes(`on${event.type}`)) { event.preventDefault(); } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index e1e3b6365..20119dd25 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -55,13 +55,17 @@ impl WebsysDom { let element = walk_event_for_id(event); let bubbles = dioxus_html::event_bubbles(name.as_str()); if let Some((element, target)) = element { - if target + if let Some(prevent_requests) = target .get_attribute("dioxus-prevent-default") .as_deref() - .map(|f| f.trim_start_matches("on")) - == Some(&name) + .map(|f| f.split_whitespace()) { - event.prevent_default(); + if prevent_requests + .map(|f| f.trim_start_matches("on")) + .any(|f| f == name) + { + event.prevent_default(); + } } let data = virtual_event_from_websys_event(event.clone(), target); From f044bb212889eb2539286ed28c9f37f4c4f3d03c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 27 Apr 2023 15:19:10 -0500 Subject: [PATCH 380/516] fix onclick event --- packages/desktop/src/file_upload.rs | 5 +- packages/desktop/src/protocol.rs | 16 +-- packages/interpreter/src/interpreter.js | 129 ++++-------------------- 3 files changed, 32 insertions(+), 118 deletions(-) diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index ae4a81cc2..1b24ffc55 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -4,7 +4,8 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub(crate) struct FileDiologRequest { - accept: String, + #[serde(default)] + accept: Option, multiple: bool, pub event: String, pub target: usize, @@ -16,6 +17,8 @@ pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec { let filters: Vec<_> = request .accept + .as_deref() + .unwrap_or_default() .split(',') .filter_map(|s| Filters::from_str(s).ok()) .collect(); diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index e35d67bb1..2e407b258 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -15,12 +15,12 @@ fn module_loader(root_name: &str) -> String { let inputs = document.querySelectorAll("input"); for (let input of inputs) { if (!input.getAttribute("data-dioxus-file-listener")) { - input.setAttribute("data-dioxus-file-listener", true); - input.addEventListener("click", (event) => { - let target = event.target; - // prevent file inputs from opening the file dialog on click - const type = target.getAttribute("type"); - if (type === "file") { + // prevent file inputs from opening the file dialog on click + const type = input.getAttribute("type"); + if (type === "file") { + input.setAttribute("data-dioxus-file-listener", true); + input.addEventListener("click", (event) => { + let target = event.target; let target_id = find_real_id(target); if (target_id !== null) { const send = (event_name) => { @@ -30,8 +30,8 @@ fn module_loader(root_name: &str) -> String { send("change&input"); } event.preventDefault(); - } - }); + }); + } } }"#, ); diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index d7b74a63e..e3c8579a4 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -346,106 +346,9 @@ class Interpreter { case "NewEventListener": let bubbles = event_bubbles(edit.name); - // this handler is only provided on desktop implementations since this - // method is not used by the web implementation - let handler = (event) => { - let target = event.target; - if (target != null) { - let realId = target.getAttribute(`data-dioxus-id`); - let preventDefaultRequests = target.getAttribute( - `dioxus-prevent-default` - ); - - if (event.type === "click") { - // todo call prevent default if it's the right type of event - let a_element = target.closest("a"); - if (a_element != null) { - event.preventDefault(); - - let elementShouldPreventDefault = - preventDefaultRequests.includes(`onclick`); - let linkShouldPreventDefault = a_element - .getAttribute(`dioxus-prevent-default`) - .includes(`onclick`); - - if (!elementShouldPreventDefault && !linkShouldPreventDefault) { - const href = a_element.getAttribute("href"); - if (href !== "" && href !== null && href !== undefined) { - window.ipc.postMessage( - serializeIpcMessage("browser_open", { href }) - ); - } - } - } - - // also prevent buttons from submitting - if (target.tagName === "BUTTON" && event.type == "submit") { - event.preventDefault(); - } - } - // walk the tree to find the real element - while (realId == null) { - // we've reached the root we don't want to send an event - if (target.parentElement === null) { - return; - } - - target = target.parentElement; - realId = target.getAttribute(`data-dioxus-id`); - } - - preventDefaultRequests = target.getAttribute( - `dioxus-prevent-default` - ); - - let contents = serialize_event(event); - - if (preventDefaultRequests.includes(`on${event.type}`)) { - event.preventDefault(); - } - - if (event.type === "submit") { - event.preventDefault(); - } - - if ( - target.tagName === "FORM" && - (event.type === "submit" || event.type === "input") - ) { - for (let x = 0; x < target.elements.length; x++) { - let element = target.elements[x]; - let name = element.getAttribute("name"); - if (name != null) { - if (element.getAttribute("type") === "checkbox") { - // @ts-ignore - contents.values[name] = element.checked ? "true" : "false"; - } else if (element.getAttribute("type") === "radio") { - if (element.checked) { - contents.values[name] = element.value; - } - } else { - // @ts-ignore - contents.values[name] = - element.value ?? element.textContent; - } - } - } - } - - if (realId === null) { - return; - } - window.ipc.postMessage( - serializeIpcMessage("user_event", { - name: edit.name, - element: parseInt(realId), - data: contents, - bubbles, - }) - ); - } - }; - this.NewEventListener(edit.name, edit.id, bubbles, handler); + this.NewEventListener(edit.name, edit.id, bubbles, (event) => { + handler(event, edit.name, bubbles); + }); break; } } @@ -456,17 +359,24 @@ class Interpreter { function handler(event, name, bubbles) { let target = event.target; if (target != null) { - let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`); + let preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`); if (event.type === "click") { - // Prevent redirects from links + // todo call prevent default if it's the right type of event let a_element = target.closest("a"); if (a_element != null) { event.preventDefault(); - if ( - shouldPreventDefault !== `onclick` && - a_element.getAttribute(`dioxus-prevent-default`) !== `onclick` - ) { + + let elementShouldPreventDefault = + preventDefaultRequests && preventDefaultRequests.includes(`onclick`); + let aElementShouldPreventDefault = a_element.getAttribute( + `dioxus-prevent-default` + ); + let linkShouldPreventDefault = + aElementShouldPreventDefault && + aElementShouldPreventDefault.includes(`onclick`); + + if (!elementShouldPreventDefault && !linkShouldPreventDefault) { const href = a_element.getAttribute("href"); if (href !== "" && href !== null && href !== undefined) { window.ipc.postMessage( @@ -484,9 +394,10 @@ function handler(event, name, bubbles) { const realId = find_real_id(target); - shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`); - - if (shouldPreventDefault === `on${event.type}`) { + if ( + preventDefaultRequests && + preventDefaultRequests.includes(`on${event.type}`) + ) { event.preventDefault(); } From f381e56ab396542bb4955c9afe3875dc67707b6b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 27 Apr 2023 16:38:09 -0500 Subject: [PATCH 381/516] test scafolding --- packages/desktop/Cargo.toml | 6 ++++++ packages/desktop/headless_tests/main.rs | 28 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 packages/desktop/headless_tests/main.rs diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 8b41019e1..af5f3e68f 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -57,4 +57,10 @@ hot-reload = ["dioxus-hot-reload"] [dev-dependencies] dioxus-core-macro = { path = "../core-macro" } dioxus-hooks = { path = "../hooks" } +dioxus = { path = "../dioxus" } # image = "0.24.0" # enable this when generating a new desktop image + +[[test]] +name = "headless_tests" +path = "headless_tests/main.rs" +harness = false \ No newline at end of file diff --git a/packages/desktop/headless_tests/main.rs b/packages/desktop/headless_tests/main.rs new file mode 100644 index 000000000..e07cb08c3 --- /dev/null +++ b/packages/desktop/headless_tests/main.rs @@ -0,0 +1,28 @@ +// Check that all events are being forwarded to the mock. +//! This example roughly shows how events are serialized into Rust from JavaScript. +//! +//! There is some conversion happening when input types are checkbox/radio/select/textarea etc. + +use dioxus::prelude::*; + +mod events; + +fn main() { + events::test_events(); +} + +pub(crate) fn check_app_exits(app: Component) { + // This is a deadman's switch to ensure that the app exits + let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); + let should_panic_clone = should_panic.clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(100)); + if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) { + panic!("App did not exit successfully") + } + }); + + dioxus_desktop::launch(app); + + should_panic.store(false, std::sync::atomic::Ordering::SeqCst); +} From 01d673e6544c26395d8d719c19a507af2c95f611 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 27 Apr 2023 18:00:43 -0500 Subject: [PATCH 382/516] create event tests --- packages/desktop/Cargo.toml | 2 +- packages/desktop/headless_tests/events.rs | 290 ++++++++++++++++++++++ packages/desktop/headless_tests/main.rs | 4 +- 3 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 packages/desktop/headless_tests/events.rs diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index af5f3e68f..e8812df5a 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -58,7 +58,7 @@ hot-reload = ["dioxus-hot-reload"] dioxus-core-macro = { path = "../core-macro" } dioxus-hooks = { path = "../hooks" } dioxus = { path = "../dioxus" } -# image = "0.24.0" # enable this when generating a new desktop image +exitcode = "1.1.2" [[test]] name = "headless_tests" diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs new file mode 100644 index 000000000..34e29f024 --- /dev/null +++ b/packages/desktop/headless_tests/events.rs @@ -0,0 +1,290 @@ +use crate::check_app_exits; +use dioxus::prelude::*; +use dioxus_desktop::DesktopContext; +use dioxus::html::geometry::euclid::Vector3D; + +pub fn test_events() { + check_app_exits(app); +} + +fn mock_event(cx: &ScopeState, id: &'static str, value: &'static str) { + use_effect(cx, (), |_| { + let desktop_context: DesktopContext = cx.consume_context().unwrap(); + async move { + desktop_context.eval(&format!( + r#"let element = document.getElementById('{}'); + // Dispatch a synthetic event + const event = {}; + element.dispatchEvent(event); + "#, + id, value + )); + } + }); +} + +#[allow(deprecated)] +fn app(cx: Scope) -> Element { + let desktop_context: DesktopContext = cx.consume_context().unwrap(); + let recieved_events = use_state(cx, || 0); + + // button + mock_event( + &cx, + "button", + r#"new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + button: 0, + })"#, + ); + // mouse_move_div + mock_event( + &cx, + "mouse_move_div", + r#"new MouseEvent("mousemove", { + view: window, + bubbles: true, + cancelable: true, + buttons: 2, + })"#, + ); + // mouse_click_div + mock_event( + &cx, + "mouse_click_div", + r#"new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + buttons: 2, + button: 2, + })"# + ); + // mouse_dblclick_div + mock_event( + &cx, + "mouse_dblclick_div", + r#"new MouseEvent("dblclick", { + view: window, + bubbles: true, + cancelable: true, + buttons: 1|2, + button: 2, + })"#, + ); + // mouse_down_div + mock_event( + &cx, + "mouse_down_div", + r#"new MouseEvent("mousedown", { + view: window, + bubbles: true, + cancelable: true, + buttons: 2, + button: 2, + })"#, + ); + // mouse_up_div + mock_event( + &cx, + "mouse_up_div", + r#"new MouseEvent("mouseup", { + view: window, + bubbles: true, + cancelable: true, + buttons: 0, + button: 0, + })"#, + ); + // wheel_div + mock_event( + &cx, + "wheel_div", + r#"new WheelEvent("wheel", { + deltaX: 1.0, + deltaY: 2.0, + deltaZ: 3.0, + })"#, + ); + // key_down_div + mock_event( + &cx, + "key_down_div", + r#"new KeyboardEvent("keydown", { + key: "a", + code: "KeyA", + location: 0, + repeat: true, + })"#, + ); + // key_up_div + mock_event( + &cx, + "key_up_div", + r#"new KeyboardEvent("keyup", { + key: "a", + code: "KeyA", + location: 0, + repeat: false, + })"#, + ); + // key_press_div + mock_event( + &cx, + "key_press_div", + r#"new KeyboardEvent("keypress", { + key: "a", + code: "KeyA", + location: 0, + repeat: false, + })"#, + ); + // focus_in_div + mock_event( + &cx, + "focus_in_div", + r#"new FocusEvent("focusin")"#, + ); + // focus_out_div + mock_event( + &cx, + "focus_out_div", + r#"new FocusEvent("focusout")"#, + ); + + + if **recieved_events == 12 { + desktop_context.close(); + } + + cx.render(rsx! { + div { + button { + id: "button", + onclick: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert!(event.data.held_buttons().is_empty()); + assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary)); + recieved_events.modify(|x| *x + 1) + }, + } + div { + id: "mouse_move_div", + onmousemove: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); + recieved_events.modify(|x| *x + 1) + }, + } + div { + id: "mouse_click_div", + onclick: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); + assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary)); + recieved_events.modify(|x| *x + 1) + }, + } + div{ + id: "mouse_dblclick_div", + ondblclick: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary)); + assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); + assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary)); + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "mouse_down_div", + onmousedown: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary)); + assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary)); + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "mouse_up_div", + onmouseup: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert!(event.data.held_buttons().is_empty()); + assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary)); + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "wheel_div", + onwheel: move |event| { + println!("{:?}", event.data); + let dioxus_html::geometry::WheelDelta::Pixels(delta)= event.data.delta()else{ + panic!("Expected delta to be in pixels") + }; + assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0)); + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "key_down_div", + onkeydown: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert_eq!(event.data.key().to_string(), "a"); + assert_eq!(event.data.code().to_string(), "KeyA"); + assert_eq!(event.data.location, 0); + assert!(event.data.is_auto_repeating()); + + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "key_up_div", + onkeyup: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert_eq!(event.data.key().to_string(), "a"); + assert_eq!(event.data.code().to_string(), "KeyA"); + assert_eq!(event.data.location, 0); + assert!(!event.data.is_auto_repeating()); + + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "key_press_div", + onkeypress: move |event| { + println!("{:?}", event.data); + assert!(event.data.modifiers().is_empty()); + assert_eq!(event.data.key().to_string(), "a"); + assert_eq!(event.data.code().to_string(), "KeyA"); + assert_eq!(event.data.location, 0); + assert!(!event.data.is_auto_repeating()); + + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "focus_in_div", + onfocusin: move |event| { + println!("{:?}", event.data); + recieved_events.modify(|x| *x + 1) + } + } + div{ + id: "focus_out_div", + onfocusout: move |event| { + println!("{:?}", event.data); + recieved_events.modify(|x| *x + 1) + } + } + } + }) +} diff --git a/packages/desktop/headless_tests/main.rs b/packages/desktop/headless_tests/main.rs index e07cb08c3..65b6c80a3 100644 --- a/packages/desktop/headless_tests/main.rs +++ b/packages/desktop/headless_tests/main.rs @@ -16,9 +16,9 @@ pub(crate) fn check_app_exits(app: Component) { let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); let should_panic_clone = should_panic.clone(); std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_secs(100)); + std::thread::sleep(std::time::Duration::from_secs(10)); if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) { - panic!("App did not exit successfully") + std::process::exit(exitcode::SOFTWARE); } }); From 70687748d3577449d101e41a13a033c6cc76aa24 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 27 Apr 2023 18:36:28 -0500 Subject: [PATCH 383/516] fix events test --- packages/desktop/headless_tests/events.rs | 55 ++++++++++++++++++++--- packages/desktop/headless_tests/main.rs | 2 +- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index 34e29f024..60962c868 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -11,10 +11,12 @@ fn mock_event(cx: &ScopeState, id: &'static str, value: &'static str) { use_effect(cx, (), |_| { let desktop_context: DesktopContext = cx.consume_context().unwrap(); async move { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; desktop_context.eval(&format!( r#"let element = document.getElementById('{}'); // Dispatch a synthetic event const event = {}; + console.log(element, event); element.dispatchEvent(event); "#, id, value @@ -103,9 +105,12 @@ fn app(cx: Scope) -> Element { &cx, "wheel_div", r#"new WheelEvent("wheel", { + view: window, deltaX: 1.0, deltaY: 2.0, deltaZ: 3.0, + deltaMode: 0x00, + bubbles: true, })"#, ); // key_down_div @@ -117,6 +122,17 @@ fn app(cx: Scope) -> Element { code: "KeyA", location: 0, repeat: true, + keyCode: 65, + charCode: 97, + char: "a", + charCode: 0, + altKey: false, + ctrlKey: false, + metaKey: false, + shiftKey: false, + isComposing: false, + which: 65, + bubbles: true, })"#, ); // key_up_div @@ -128,6 +144,17 @@ fn app(cx: Scope) -> Element { code: "KeyA", location: 0, repeat: false, + keyCode: 65, + charCode: 97, + char: "a", + charCode: 0, + altKey: false, + ctrlKey: false, + metaKey: false, + shiftKey: false, + isComposing: false, + which: 65, + bubbles: true, })"#, ); // key_press_div @@ -139,19 +166,30 @@ fn app(cx: Scope) -> Element { code: "KeyA", location: 0, repeat: false, + keyCode: 65, + charCode: 97, + char: "a", + charCode: 0, + altKey: false, + ctrlKey: false, + metaKey: false, + shiftKey: false, + isComposing: false, + which: 65, + bubbles: true, })"#, ); // focus_in_div mock_event( &cx, "focus_in_div", - r#"new FocusEvent("focusin")"#, + r#"new FocusEvent("focusin", {bubbles: true})"#, ); // focus_out_div mock_event( &cx, "focus_out_div", - r#"new FocusEvent("focusout")"#, + r#"new FocusEvent("focusout",{bubbles: true})"#, ); @@ -223,6 +261,9 @@ fn app(cx: Scope) -> Element { } div{ id: "wheel_div", + width: "100px", + height: "100px", + background_color: "red", onwheel: move |event| { println!("{:?}", event.data); let dioxus_html::geometry::WheelDelta::Pixels(delta)= event.data.delta()else{ @@ -232,7 +273,7 @@ fn app(cx: Scope) -> Element { recieved_events.modify(|x| *x + 1) } } - div{ + input{ id: "key_down_div", onkeydown: move |event| { println!("{:?}", event.data); @@ -245,7 +286,7 @@ fn app(cx: Scope) -> Element { recieved_events.modify(|x| *x + 1) } } - div{ + input{ id: "key_up_div", onkeyup: move |event| { println!("{:?}", event.data); @@ -258,7 +299,7 @@ fn app(cx: Scope) -> Element { recieved_events.modify(|x| *x + 1) } } - div{ + input{ id: "key_press_div", onkeypress: move |event| { println!("{:?}", event.data); @@ -271,14 +312,14 @@ fn app(cx: Scope) -> Element { recieved_events.modify(|x| *x + 1) } } - div{ + input{ id: "focus_in_div", onfocusin: move |event| { println!("{:?}", event.data); recieved_events.modify(|x| *x + 1) } } - div{ + input{ id: "focus_out_div", onfocusout: move |event| { println!("{:?}", event.data); diff --git a/packages/desktop/headless_tests/main.rs b/packages/desktop/headless_tests/main.rs index 65b6c80a3..a9d9ec61c 100644 --- a/packages/desktop/headless_tests/main.rs +++ b/packages/desktop/headless_tests/main.rs @@ -16,7 +16,7 @@ pub(crate) fn check_app_exits(app: Component) { let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); let should_panic_clone = should_panic.clone(); std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_secs(10)); + std::thread::sleep(std::time::Duration::from_secs(100)); if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) { std::process::exit(exitcode::SOFTWARE); } From 0726a7b08f5e499869c114e37aae973d029d6ad3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 27 Apr 2023 20:25:16 -0500 Subject: [PATCH 384/516] update server functions commit --- packages/server/Cargo.toml | 2 +- packages/server/server-macro/Cargo.toml | 2 +- packages/server/server-macro/src/lib.rs | 5 +- packages/server/src/adapters/axum_adapter.rs | 39 +++++++---- packages/server/src/adapters/salvo_adapter.rs | 44 ++++++++----- packages/server/src/adapters/warp_adapter.rs | 66 ++++++++++++++----- packages/server/src/lib.rs | 2 +- packages/server/src/server_fn.rs | 36 ++++++++-- 8 files changed, 138 insertions(+), 58 deletions(-) diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 18633e97a..d182aa415 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"] [dependencies] # server functions -server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] } +server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] } dioxus_server_macro = { path = "server-macro" } # warp diff --git a/packages/server/server-macro/Cargo.toml b/packages/server/server-macro/Cargo.toml index 45b0ec3c6..286481bdd 100644 --- a/packages/server/server-macro/Cargo.toml +++ b/packages/server/server-macro/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] quote = "1.0.26" -server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "a9e6590b5e7f1c0b01da7db7b86719cb18a4aaa1", features = ["stable"] } +server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] } syn = { version = "1", features = ["full"] } [lib] diff --git a/packages/server/server-macro/src/lib.rs b/packages/server/server-macro/src/lib.rs index 14fc0ac77..b3f890f6a 100644 --- a/packages/server/server-macro/src/lib.rs +++ b/packages/server/server-macro/src/lib.rs @@ -14,9 +14,10 @@ use server_fn_macro::*; /// 2. *Optional*: A URL prefix at which the function will be mounted when it’s registered /// (e.g., `"/api"`). Defaults to `"/"`. /// 3. *Optional*: either `"Cbor"` (specifying that it should use the binary `cbor` format for -/// serialization) or `"Url"` (specifying that it should be use a URL-encoded form-data string). +/// serialization), `"Url"` (specifying that it should be use a URL-encoded form-data string). /// Defaults to `"Url"`. If you want to use this server function to power a `` that will -/// work without WebAssembly, the encoding must be `"Url"`. +/// work without WebAssembly, the encoding must be `"Url"`. If you want to use this server function +/// using Get instead of Post methods, the encoding must be `"GetCbor"` or `"GetJson"`. /// /// The server function itself can take any number of arguments, each of which should be serializable /// and deserializable with `serde`. Optionally, its first argument can be a [DioxusServerContext](https::/docs.rs/dioxus-server/latest/dixous_server/prelude/struct.DioxusServerContext.html), diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index f4bdd8a96..2d16e9491 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -55,10 +55,9 @@ //! } //! ``` -use std::{error::Error, sync::Arc}; - use axum::{ body::{self, Body, BoxBody, Full}, + extract::RawQuery, extract::{State, WebSocketUpgrade}, handler::Handler, http::{HeaderMap, Request, Response, StatusCode}, @@ -66,14 +65,13 @@ use axum::{ routing::{get, post}, Router, }; -use server_fn::{Payload, ServerFunctionRegistry}; +use server_fn::{Encoding, Payload, ServerFunctionRegistry}; +use std::error::Error; use tokio::task::spawn_blocking; use crate::{ - render::SSRState, - serve_config::ServeConfig, - server_context::DioxusServerContext, - server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, + prelude::*, render::SSRState, serve_config::ServeConfig, server_context::DioxusServerContext, + server_fn::DioxusServerFnRegistry, }; /// A extension trait with utilities for integrating Dioxus with your Axum router. @@ -106,7 +104,7 @@ pub trait DioxusRouterExt { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - handler: impl Fn(Arc) -> H, + handler: impl Fn(ServerFunction) -> H, ) -> Self where H: Handler, @@ -200,7 +198,7 @@ where fn register_server_fns_with_handler( self, server_fn_route: &'static str, - mut handler: impl FnMut(Arc) -> H, + mut handler: impl FnMut(ServerFunction) -> H, ) -> Self where H: Handler, @@ -211,15 +209,22 @@ where for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - router = router.route(&full_route, post(handler(func))); + match func.encoding { + Encoding::Url | Encoding::Cbor => { + router = router.route(&full_route, post(handler(func))); + } + Encoding::GetJSON | Encoding::GetCBOR => { + router = router.route(&full_route, get(handler(func))); + } + } } router } fn register_server_fns(self, server_fn_route: &'static str) -> Self { self.register_server_fns_with_handler(server_fn_route, |func| { - move |headers: HeaderMap, body: Request| async move { - server_fn_handler((), func.clone(), headers, body).await + move |headers: HeaderMap, RawQuery(query): RawQuery, body: Request| async move { + server_fn_handler((), func.clone(), headers, query, body).await } }) } @@ -299,8 +304,9 @@ async fn render_handler( /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: impl Into, - function: Arc, + function: ServerFunction, headers: HeaderMap, + query: Option, req: Request, ) -> impl IntoResponse { let server_context = server_context.into(); @@ -317,7 +323,12 @@ pub async fn server_fn_handler( tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async { - let resp = match function(server_context, &body).await { + let query = &query.unwrap_or_default().into(); + let data = match &function.encoding { + Encoding::Url | Encoding::Cbor => &body, + Encoding::GetJSON | Encoding::GetCBOR => query, + }; + let resp = match (function.trait_obj)(server_context, &data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response let accept_header = diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index ffe72bef5..402a0b0a0 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -50,7 +50,7 @@ //! } //! ``` -use std::{error::Error, sync::Arc}; +use std::error::Error; use hyper::{http::HeaderValue, StatusCode}; use salvo::{ @@ -58,14 +58,11 @@ use salvo::{ serve_static::{StaticDir, StaticFile}, Depot, FlowCtrl, Handler, Request, Response, Router, }; -use server_fn::{Payload, ServerFunctionRegistry}; +use server_fn::{Encoding, Payload, ServerFunctionRegistry}; use tokio::task::spawn_blocking; use crate::{ - prelude::DioxusServerContext, - render::SSRState, - serve_config::ServeConfig, - server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, + prelude::*, render::SSRState, serve_config::ServeConfig, server_fn::DioxusServerFnRegistry, }; /// A extension trait with utilities for integrating Dioxus with your Salvo router. @@ -79,7 +76,7 @@ pub trait DioxusRouterExt { /// use dioxus_server::prelude::*; /// /// struct ServerFunctionHandler { - /// server_fn: Arc, + /// server_fn: ServerFunction, /// } /// /// #[handler] @@ -112,7 +109,7 @@ pub trait DioxusRouterExt { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - handler: impl Fn(Arc) -> H, + handler: impl Fn(ServerFunction) -> H, ) -> Self where H: Handler + 'static; @@ -187,7 +184,7 @@ impl DioxusRouterExt for Router { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - mut handler: impl FnMut(Arc) -> H, + mut handler: impl FnMut(ServerFunction) -> H, ) -> Self where H: Handler + 'static, @@ -196,7 +193,14 @@ impl DioxusRouterExt for Router { for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - router = router.push(Router::with_path(&full_route).post(handler(func))); + match func.encoding { + Encoding::Url | Encoding::Cbor => { + router = router.push(Router::with_path(&full_route).post(handler(func))); + } + Encoding::GetJSON | Encoding::GetCBOR => { + router = router.push(Router::with_path(&full_route).get(handler(func))); + } + } } router } @@ -288,15 +292,12 @@ impl Handler for SSRHandler

{ /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub struct ServerFnHandler { server_context: DioxusServerContext, - function: Arc, + function: ServerFunction, } impl ServerFnHandler { /// Create a new server function handler with the given server context and server function. - pub fn new( - server_context: impl Into, - function: Arc, - ) -> Self { + pub fn new(server_context: impl Into, function: ServerFunction) -> Self { let server_context = server_context.into(); Self { server_context, @@ -324,12 +325,23 @@ impl ServerFnHandler { let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); let function = function.clone(); let server_context = server_context.clone(); + let query = req + .uri() + .query() + .unwrap_or_default() + .as_bytes() + .to_vec() + .into(); spawn_blocking({ move || { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async move { - let resp = function(server_context, &body).await; + let data = match &function.encoding { + Encoding::Url | Encoding::Cbor => &body, + Encoding::GetJSON | Encoding::GetCBOR => &query, + }; + let resp = (function.trait_obj)(server_context, data).await; resp_tx.send(resp).unwrap(); }) diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 5041e294d..db88ac2b4 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -47,9 +47,12 @@ //! //! ``` -use std::{error::Error, sync::Arc}; +use crate::{ + prelude::*, render::SSRState, serve_config::ServeConfig, server_fn::DioxusServerFnRegistry, +}; -use server_fn::{Payload, ServerFunctionRegistry}; +use server_fn::{Encoding, Payload, ServerFunctionRegistry}; +use std::error::Error; use tokio::task::spawn_blocking; use warp::{ filters::BoxedFilter, @@ -58,13 +61,6 @@ use warp::{ path, Filter, Reply, }; -use crate::{ - prelude::DioxusServerContext, - render::SSRState, - serve_config::ServeConfig, - server_fn::{DioxusServerFnRegistry, ServerFnTraitObj}, -}; - /// Registers server functions with a custom handler function. This allows you to pass custom context to your server functions by generating a [`DioxusServerContext`] from the request. /// /// # Example @@ -94,7 +90,7 @@ pub fn register_server_fns_with_handler( mut handler: H, ) -> BoxedFilter<(R,)> where - H: FnMut(String, Arc) -> F, + H: FnMut(String, ServerFunction) -> F, F: Filter + Send + Sync + 'static, F::Extract: Send, R: Reply + 'static, @@ -129,16 +125,48 @@ where /// ``` pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { register_server_fns_with_handler(server_fn_route, |full_route, func| { - path(full_route) - .and(warp::post()) + let func2 = func.clone(); + let func3 = func.clone(); + path(full_route.clone()) + .and(warp::filters::method::get()) .and(warp::header::headers_cloned()) + .and(warp::filters::query::raw()) .and(warp::body::bytes()) - .and_then(move |headers: HeaderMap, body| { + .and_then(move |headers, query, body| { let func = func.clone(); async move { - server_fn_handler(DioxusServerContext::default(), func, headers, body).await + server_fn_handler( + DioxusServerContext::default(), + func, + headers, + Some(query), + body, + ) + .await } }) + .or(path(full_route.clone()) + .and(warp::filters::method::get()) + .and(warp::header::headers_cloned()) + .and(warp::body::bytes()) + .and_then(move |headers, body| { + let func = func2.clone(); + async move { + server_fn_handler(DioxusServerContext::default(), func, headers, None, body) + .await + } + })) + .or(path(full_route) + .and(warp::filters::method::post()) + .and(warp::header::headers_cloned()) + .and(warp::body::bytes()) + .and_then(move |headers, body| { + let func = func3.clone(); + async move { + server_fn_handler(DioxusServerContext::default(), func, headers, None, body) + .await + } + })) }) } @@ -198,8 +226,9 @@ impl warp::reject::Reject for RecieveFailed {} /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: impl Into, - function: Arc, + function: ServerFunction, headers: HeaderMap, + query: Option, body: Bytes, ) -> Result, warp::Rejection> { let server_context = server_context.into(); @@ -210,7 +239,12 @@ pub async fn server_fn_handler( tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async { - let resp = match function(server_context, &body).await { + let query = &query.unwrap_or_default().into(); + let data = match &function.encoding { + Encoding::Url | Encoding::Cbor => &body, + Encoding::GetJSON | Encoding::GetCBOR => query, + }; + let resp = match (function.trait_obj)(server_context, &data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response let accept_header = diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index df2735cf4..525b636b3 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -30,7 +30,7 @@ pub mod prelude { pub use crate::server_context::DioxusServerContext; pub use crate::server_fn::ServerFn; #[cfg(feature = "ssr")] - pub use crate::server_fn::ServerFnTraitObj; + pub use crate::server_fn::{ServerFnTraitObj, ServerFunction}; pub use dioxus_server_macro::*; pub use server_fn::{self, ServerFn as _, ServerFnError}; } diff --git a/packages/server/src/server_fn.rs b/packages/server/src/server_fn.rs index 643f25c9f..da7311263 100644 --- a/packages/server/src/server_fn.rs +++ b/packages/server/src/server_fn.rs @@ -4,14 +4,14 @@ use crate::server_context::DioxusServerContext; /// A trait object for a function that be called on serializable arguments and returns a serializable result. pub type ServerFnTraitObj = server_fn::ServerFnTraitObj; +#[cfg(any(feature = "ssr", doc))] +/// A server function that can be called on serializable arguments and returns a serializable result. +pub type ServerFunction = server_fn::ServerFunction; + #[cfg(any(feature = "ssr", doc))] #[allow(clippy::type_complexity)] static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy< - std::sync::Arc< - std::sync::RwLock< - std::collections::HashMap<&'static str, std::sync::Arc>, - >, - >, + std::sync::Arc>>, > = once_cell::sync::Lazy::new(Default::default); #[cfg(any(feature = "ssr", doc))] @@ -25,12 +25,19 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe fn register( url: &'static str, server_function: std::sync::Arc, + encoding: server_fn::Encoding, ) -> Result<(), Self::Error> { // store it in the hashmap let mut write = REGISTERED_SERVER_FUNCTIONS .write() .map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?; - let prev = write.insert(url, server_function); + let prev = write.insert( + url, + ServerFunction { + trait_obj: server_function, + encoding, + }, + ); // if there was already a server function with this key, // return Err @@ -47,13 +54,28 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe } /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. - fn get(url: &str) -> Option> { + fn get(url: &str) -> Option { REGISTERED_SERVER_FUNCTIONS .read() .ok() .and_then(|fns| fns.get(url).cloned()) } + /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. + fn get_trait_obj(url: &str) -> Option> { + REGISTERED_SERVER_FUNCTIONS + .read() + .ok() + .and_then(|fns| fns.get(url).map(|f| f.trait_obj.clone())) + } + + fn get_encoding(url: &str) -> Option { + REGISTERED_SERVER_FUNCTIONS + .read() + .ok() + .and_then(|fns| fns.get(url).map(|f| f.encoding.clone())) + } + /// Returns a list of all registered server functions. fn paths_registered() -> Vec<&'static str> { REGISTERED_SERVER_FUNCTIONS From 03a282466026b0bb68b9e6581c3e6ccec9fd0ac8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 28 Apr 2023 17:59:01 -0500 Subject: [PATCH 385/516] allow passing root prop data from the server to the client --- packages/server/Cargo.toml | 5 ++ .../examples/axum-hello-world/src/main.rs | 21 ++++- .../examples/salvo-hello-world/src/main.rs | 22 +++-- .../examples/warp-hello-world/src/main.rs | 21 ++++- packages/server/src/adapters/axum_adapter.rs | 6 +- packages/server/src/adapters/salvo_adapter.rs | 6 +- packages/server/src/adapters/warp_adapter.rs | 2 +- packages/server/src/lib.rs | 4 + .../src/props_html/deserialize_props.rs | 37 ++++++++ packages/server/src/props_html/mod.rs | 85 +++++++++++++++++++ .../server/src/props_html/serialize_props.rs | 37 ++++++++ packages/server/src/render.rs | 5 +- 12 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 packages/server/src/props_html/deserialize_props.rs create mode 100644 packages/server/src/props_html/mod.rs create mode 100644 packages/server/src/props_html/serialize_props.rs diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index d182aa415..2e20cdc81 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -44,10 +44,15 @@ anymap = "0.12.1" serde_json = { version = "1.0.95", optional = true } tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } futures-util = { version = "0.3.28", optional = true } +postcard = { version = "1.0.4", features = ["use-std"] } +yazi = "0.1.5" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] dioxus-hot-reload = { path = "../hot-reload" } +[target.'cfg(target_arch = "wasm32")'.dependencies] +web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } + [features] default = ["hot-reload"] hot-reload = ["serde_json", "tokio-stream", "futures-util"] diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index 899247af5..b5553f7a2 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -8,10 +8,15 @@ #![allow(non_snake_case)] use dioxus::prelude::*; use dioxus_server::prelude::*; +use serde::{Deserialize, Serialize}; fn main() { #[cfg(feature = "web")] - dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + dioxus_web::launch_with_props( + app, + get_props_from_document().unwrap_or_default(), + dioxus_web::Config::new().hydrate(true), + ); #[cfg(feature = "ssr")] { PostServerData::register().unwrap(); @@ -23,7 +28,10 @@ fn main() { axum::Server::bind(&addr) .serve( axum::Router::new() - .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) + .serve_dioxus_application( + "", + ServeConfigBuilder::new(app, AppProps { count: 12345 }).build(), + ) .into_make_service(), ) .await @@ -32,8 +40,13 @@ fn main() { } } -fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || 0); +#[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)] +struct AppProps { + count: i32, +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || cx.props.count); let text = use_state(cx, || "...".to_string()); cx.render(rsx! { diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs index d1e654fee..15a3ebe31 100644 --- a/packages/server/examples/salvo-hello-world/src/main.rs +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -8,10 +8,15 @@ #![allow(non_snake_case)] use dioxus::prelude::*; use dioxus_server::prelude::*; +use serde::{Deserialize, Serialize}; fn main() { #[cfg(feature = "web")] - dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + dioxus_web::launch_with_props( + app, + get_props_from_document().unwrap_or_default(), + dioxus_web::Config::new().hydrate(true), + ); #[cfg(feature = "ssr")] { use salvo::prelude::*; @@ -20,8 +25,10 @@ fn main() { tokio::runtime::Runtime::new() .unwrap() .block_on(async move { - let router = - Router::new().serve_dioxus_application("", ServeConfigBuilder::new(app, ())); + let router = Router::new().serve_dioxus_application( + "", + ServeConfigBuilder::new(app, AppProps { count: 12345 }), + ); Server::new(TcpListener::bind("127.0.0.1:8080")) .serve(router) .await; @@ -29,8 +36,13 @@ fn main() { } } -fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || 0); +#[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)] +struct AppProps { + count: i32, +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || cx.props.count); let text = use_state(cx, || "...".to_string()); cx.render(rsx! { diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs index 51a627d16..a636dbc58 100644 --- a/packages/server/examples/warp-hello-world/src/main.rs +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -8,10 +8,15 @@ #![allow(non_snake_case)] use dioxus::prelude::*; use dioxus_server::prelude::*; +use serde::{Deserialize, Serialize}; fn main() { #[cfg(feature = "web")] - dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); + dioxus_web::launch_with_props( + app, + get_props_from_document().unwrap_or_default(), + dioxus_web::Config::new().hydrate(true), + ); #[cfg(feature = "ssr")] { PostServerData::register().unwrap(); @@ -19,14 +24,22 @@ fn main() { tokio::runtime::Runtime::new() .unwrap() .block_on(async move { - let routes = serve_dioxus_application("", ServeConfigBuilder::new(app, ())); + let routes = serve_dioxus_application( + "", + ServeConfigBuilder::new(app, AppProps { count: 12345 }), + ); warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; }); } } -fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || 0); +#[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)] +struct AppProps { + count: i32, +} + +fn app(cx: Scope) -> Element { + let mut count = use_state(cx, || cx.props.count); let text = use_state(cx, || "...".to_string()); cx.render(rsx! { diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 2d16e9491..7fc47a8b3 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -184,7 +184,7 @@ pub trait DioxusRouterExt { /// todo!() /// } /// ``` - fn serve_dioxus_application( + fn serve_dioxus_application( self, server_fn_route: &'static str, cfg: impl Into>, @@ -229,7 +229,7 @@ where }) } - fn serve_dioxus_application( + fn serve_dioxus_application( mut self, server_fn_route: &'static str, cfg: impl Into>, @@ -294,7 +294,7 @@ where } } -async fn render_handler( +async fn render_handler( State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, ) -> impl IntoResponse { let rendered = ssr_state.render(&cfg); diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 402a0b0a0..ca11ff9ec 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -173,7 +173,7 @@ pub trait DioxusRouterExt { /// /// fn app(cx: Scope) -> Element {todo!()} /// ``` - fn serve_dioxus_application( + fn serve_dioxus_application( self, server_fn_path: &'static str, cfg: impl Into>, @@ -212,7 +212,7 @@ impl DioxusRouterExt for Router { }) } - fn serve_dioxus_application( + fn serve_dioxus_application( mut self, server_fn_route: &'static str, cfg: impl Into>, @@ -269,7 +269,7 @@ struct SSRHandler { } #[async_trait] -impl Handler for SSRHandler

{ +impl Handler for SSRHandler

{ async fn handle( &self, _req: &mut Request, diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index db88ac2b4..414160045 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -189,7 +189,7 @@ pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl R /// todo!() /// } /// ``` -pub fn serve_dioxus_application( +pub fn serve_dioxus_application( server_fn_route: &'static str, cfg: impl Into>, ) -> BoxedFilter<(impl Reply,)> { diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 525b636b3..e9f4ee63f 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -5,6 +5,8 @@ pub use adapters::*; +mod props_html; + mod adapters; #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] mod hot_reload; @@ -23,6 +25,8 @@ pub mod prelude { pub use crate::adapters::salvo_adapter::*; #[cfg(feature = "warp")] pub use crate::adapters::warp_adapter::*; + #[cfg(not(feature = "ssr"))] + pub use crate::props_html::deserialize_props::get_props_from_document; #[cfg(feature = "ssr")] pub use crate::render::SSRState; #[cfg(feature = "ssr")] diff --git a/packages/server/src/props_html/deserialize_props.rs b/packages/server/src/props_html/deserialize_props.rs new file mode 100644 index 000000000..28b206ac5 --- /dev/null +++ b/packages/server/src/props_html/deserialize_props.rs @@ -0,0 +1,37 @@ +use serde::de::DeserializeOwned; + +use super::u16_from_char; + +#[allow(unused)] +pub(crate) fn serde_from_string(string: &str) -> Option { + let decompressed = string + .chars() + .flat_map(|c| { + let u = u16_from_char(c); + let u1 = (u >> 8) as u8; + let u2 = (u & 0xFF) as u8; + [u1, u2].into_iter() + }) + .collect::>(); + let (decompressed, _) = yazi::decompress(&decompressed, yazi::Format::Zlib).unwrap(); + + postcard::from_bytes(&decompressed).ok() +} + +#[cfg(not(feature = "ssr"))] +/// Get the props from the document. This is only available in the browser. +pub fn get_props_from_document() -> Option { + #[cfg(not(target_arch = "wasm32"))] + { + None + } + #[cfg(target_arch = "wasm32")] + { + let attribute = web_sys::window()? + .document()? + .get_element_by_id("dioxus-storage")? + .get_attribute("data-serialized")?; + + serde_from_string(&attribute) + } +} diff --git a/packages/server/src/props_html/mod.rs b/packages/server/src/props_html/mod.rs new file mode 100644 index 000000000..9e9b8721e --- /dev/null +++ b/packages/server/src/props_html/mod.rs @@ -0,0 +1,85 @@ +pub(crate) mod deserialize_props; + +pub(crate) mod serialize_props; + +#[test] +fn serialized_and_deserializes() { + use postcard::to_allocvec; + + #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] + struct Data { + a: u32, + b: String, + bytes: Vec, + nested: Nested, + } + + #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] + struct Nested { + a: u32, + b: u16, + c: u8, + } + + for x in 0..10usize { + for y in 0..10 { + let mut as_string = String::new(); + let data = vec![ + Data { + a: x as u32, + b: "hello".to_string(), + bytes: vec![0; x], + nested: Nested { + a: 1, + b: x as u16, + c: 3 + }, + }; + y + ]; + serialize_props::serde_to_writable(&data, &mut as_string).unwrap(); + + println!("{}", as_string); + println!( + "original size: {}", + std::mem::size_of::() * data.len() + ); + println!("serialized size: {}", to_allocvec(&data).unwrap().len()); + println!("compressed size: {}", as_string.len()); + + let decoded: Vec = deserialize_props::serde_from_string(&as_string).unwrap(); + assert_eq!(data, decoded); + } + } +} + +#[test] +fn encodes_and_decodes_bytes() { + for i in 0..(u16::MAX) { + let c = u16_to_char(i); + let i2 = u16_from_char(c); + assert_eq!(i, i2); + } +} + +#[allow(unused)] +pub(crate) fn u16_to_char(u: u16) -> char { + let u = u as u32; + let mapped = if u <= 0xD7FF { + u + } else { + 0xE000 + (u - 0xD7FF) + }; + char::from_u32(mapped).unwrap() +} + +#[allow(unused)] +pub(crate) fn u16_from_char(c: char) -> u16 { + let c = c as u32; + let mapped = if c <= 0xD7FF { + c + } else { + 0xD7FF + (c - 0xE000) + }; + mapped as u16 +} diff --git a/packages/server/src/props_html/serialize_props.rs b/packages/server/src/props_html/serialize_props.rs new file mode 100644 index 000000000..f1282f07f --- /dev/null +++ b/packages/server/src/props_html/serialize_props.rs @@ -0,0 +1,37 @@ +use serde::Serialize; + +use super::u16_to_char; + +#[allow(unused)] +pub(crate) fn serde_to_writable( + value: &T, + mut write_to: impl std::fmt::Write, +) -> std::fmt::Result { + let serialized = postcard::to_allocvec(value).unwrap(); + let compressed = yazi::compress( + &serialized, + yazi::Format::Zlib, + yazi::CompressionLevel::BestSize, + ) + .unwrap(); + for array in compressed.chunks(2) { + let w = if array.len() == 2 { + [array[0], array[1]] + } else { + [array[0], 0] + }; + write_to.write_char(u16_to_char((w[0] as u16) << 8 | (w[1] as u16)))?; + } + Ok(()) +} + +#[cfg(feature = "ssr")] +/// Encode data into a element. This is inteded to be used in the server to send data to the client. +pub(crate) fn encode_in_element( + data: T, + mut write_to: impl std::fmt::Write, +) -> std::fmt::Result { + write_to.write_str(r#""#) +} diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 8328be317..0f61a3a0f 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -24,7 +24,7 @@ impl Default for SSRState { impl SSRState { /// Render the application to HTML. - pub fn render(&self, cfg: &ServeConfig

) -> String { + pub fn render(&self, cfg: &ServeConfig

) -> String { let ServeConfig { app, props, index, .. } = cfg; @@ -41,6 +41,9 @@ impl SSRState { let _ = renderer.render_to(&mut html, &vdom); + // serialize the props + let _ = crate::props_html::serialize_props::encode_in_element(&cfg.props, &mut html); + html += &index.post_main; html From 581a4648de58f3db9a5b617a39a9b73d7f639bf9 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 28 Apr 2023 18:01:21 -0500 Subject: [PATCH 386/516] document get_root_props_from_document --- packages/server/examples/axum-hello-world/src/main.rs | 2 +- packages/server/examples/salvo-hello-world/src/main.rs | 2 +- packages/server/examples/warp-hello-world/src/main.rs | 2 +- packages/server/src/lib.rs | 2 +- packages/server/src/props_html/deserialize_props.rs | 4 +++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index b5553f7a2..5b3323ec4 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -14,7 +14,7 @@ fn main() { #[cfg(feature = "web")] dioxus_web::launch_with_props( app, - get_props_from_document().unwrap_or_default(), + get_root_props_from_document().unwrap_or_default(), dioxus_web::Config::new().hydrate(true), ); #[cfg(feature = "ssr")] diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs index 15a3ebe31..a6e33dc63 100644 --- a/packages/server/examples/salvo-hello-world/src/main.rs +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -14,7 +14,7 @@ fn main() { #[cfg(feature = "web")] dioxus_web::launch_with_props( app, - get_props_from_document().unwrap_or_default(), + get_root_props_from_document().unwrap_or_default(), dioxus_web::Config::new().hydrate(true), ); #[cfg(feature = "ssr")] diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs index a636dbc58..4929af0c8 100644 --- a/packages/server/examples/warp-hello-world/src/main.rs +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -14,7 +14,7 @@ fn main() { #[cfg(feature = "web")] dioxus_web::launch_with_props( app, - get_props_from_document().unwrap_or_default(), + get_root_props_from_document().unwrap_or_default(), dioxus_web::Config::new().hydrate(true), ); #[cfg(feature = "ssr")] diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index e9f4ee63f..c5ad196bc 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -26,7 +26,7 @@ pub mod prelude { #[cfg(feature = "warp")] pub use crate::adapters::warp_adapter::*; #[cfg(not(feature = "ssr"))] - pub use crate::props_html::deserialize_props::get_props_from_document; + pub use crate::props_html::deserialize_props::get_root_props_from_document; #[cfg(feature = "ssr")] pub use crate::render::SSRState; #[cfg(feature = "ssr")] diff --git a/packages/server/src/props_html/deserialize_props.rs b/packages/server/src/props_html/deserialize_props.rs index 28b206ac5..64f2181ab 100644 --- a/packages/server/src/props_html/deserialize_props.rs +++ b/packages/server/src/props_html/deserialize_props.rs @@ -20,7 +20,9 @@ pub(crate) fn serde_from_string(string: &str) -> Option #[cfg(not(feature = "ssr"))] /// Get the props from the document. This is only available in the browser. -pub fn get_props_from_document() -> Option { +/// +/// When dioxus-server renders the page, it will serialize the root props and put them in the document. This function gets them from the document. +pub fn get_root_props_from_document() -> Option { #[cfg(not(target_arch = "wasm32"))] { None From 3317dc3e177e80ff62ba1b3a5f66e942d222f051 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 29 Apr 2023 07:58:43 -0500 Subject: [PATCH 387/516] fix the link to tui widgets in the guide --- docs/guide/src/en/custom_renderer/index.md | 2 +- docs/guide/src/en/getting_started/tui.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/guide/src/en/custom_renderer/index.md b/docs/guide/src/en/custom_renderer/index.md index 2d4ddb759..b531593dc 100644 --- a/docs/guide/src/en/custom_renderer/index.md +++ b/docs/guide/src/en/custom_renderer/index.md @@ -15,7 +15,7 @@ Essentially, your renderer needs to process edits and generate events to update Internally, Dioxus handles the tree relationship, diffing, memory management, and the event system, leaving as little as possible required for renderers to implement themselves. -For reference, check out the [javascript interpreter](https://github.com/DioxusLabs/dioxus/tree/master/packages/interpreter) or [tui renderer](https://github.com/DioxusLabs/dioxus/tree/master/packages/tui) as a starting point for your custom renderer. +For reference, check out the [javascript interpreter](https://github.com/DioxusLabs/dioxus/tree/master/packages/interpreter) or [tui renderer](https://github.com/DioxusLabs/dioxus/tree/master/packages/dioxus-tui) as a starting point for your custom renderer. ## Templates diff --git a/docs/guide/src/en/getting_started/tui.md b/docs/guide/src/en/getting_started/tui.md index e49cb84a8..4e8d36cf9 100644 --- a/docs/guide/src/en/getting_started/tui.md +++ b/docs/guide/src/en/getting_started/tui.md @@ -12,14 +12,12 @@ TUI support is currently quite experimental. But, if you're willing to venture i - It uses flexbox for the layout - It only supports a subset of the attributes and elements -- Regular widgets will not work in the tui render, but the tui renderer has its own widget components that start with a capital letter. See the [widgets example](https://github.com/DioxusLabs/dioxus/blob/master/packages/tui/examples/widgets.rs) +- Regular widgets will not work in the tui render, but the tui renderer has its own widget components that start with a capital letter. See the [widgets example](https://github.com/DioxusLabs/dioxus/blob/master/packages/dioxus-tui/examples/widgets.rs) - 1px is one character line height. Your regular CSS px does not translate - If your app panics, your terminal is wrecked. This will be fixed eventually - ## Getting Set up - Start by making a new package and adding Dioxus and the TUI renderer as dependancies. ```shell From 74cb70590c1b50d8f9bbaf82411e6c3c8a8223e0 Mon Sep 17 00:00:00 2001 From: stephenandary Date: Sat, 29 Apr 2023 11:44:28 -0400 Subject: [PATCH 388/516] WIP - Initial commit of devcontainer --- .devcontainer/devcontainer.json | 28 +++++++++++++++++++ .../linux/devcontainer-feature.json | 4 +++ .devcontainer/local-features/linux/install.sh | 6 ++++ 3 files changed, 38 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/local-features/linux/devcontainer-feature.json create mode 100644 .devcontainer/local-features/linux/install.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..1c5278332 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +{ + "name": "dioxus", + "image": "mcr.microsoft.com/devcontainers/rust:latest", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": {}, + "./local-features/linux": {} + }, + "containerEnv": { + "RUST_LOG": "INFO" + }, + "customizations": { + "vscode": { + "settings": { + "files.watcherExclude": { + "**/target/**": true + }, + "[rust]": { + "editor.formatOnSave": true + } + }, + "extensions": [ + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + "serayuzgur.crates" + ] + } + } +} \ No newline at end of file diff --git a/.devcontainer/local-features/linux/devcontainer-feature.json b/.devcontainer/local-features/linux/devcontainer-feature.json new file mode 100644 index 000000000..daba5bc1f --- /dev/null +++ b/.devcontainer/local-features/linux/devcontainer-feature.json @@ -0,0 +1,4 @@ +{ + "id": "linux", + "version": "0.0.1" +} \ No newline at end of file diff --git a/.devcontainer/local-features/linux/install.sh b/.devcontainer/local-features/linux/install.sh new file mode 100644 index 000000000..8f9ad5e1a --- /dev/null +++ b/.devcontainer/local-features/linux/install.sh @@ -0,0 +1,6 @@ +#!/bin/bash +echo "Update Apt Database" +sudo apt-get update + +echo "Install Webkit Dependencies" +sudo apt-get -qq install build-essential libwebkit2gtk-4.0-dev libgtk-3-dev libayatana-appindicator3-dev From 7ff5d356d5cdfbf2709fb7120e7f7088dcb6a658 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 29 Apr 2023 17:04:54 -0500 Subject: [PATCH 389/516] allow server functions to read request and modify responce --- packages/server/Cargo.toml | 9 +- .../examples/axum-hello-world/Cargo.toml | 7 + .../examples/axum-hello-world/src/main.rs | 9 +- .../server/examples/axum-router/Cargo.toml | 7 + .../examples/salvo-hello-world/Cargo.toml | 7 + .../examples/salvo-hello-world/src/main.rs | 11 +- .../examples/warp-hello-world/Cargo.toml | 7 + .../examples/warp-hello-world/src/main.rs | 11 +- packages/server/src/adapters/axum_adapter.rs | 46 ++-- packages/server/src/adapters/salvo_adapter.rs | 62 ++++-- packages/server/src/adapters/warp_adapter.rs | 145 ++++++++----- packages/server/src/lib.rs | 6 +- packages/server/src/render.rs | 15 +- packages/server/src/server_context.rs | 204 +++++++++++++----- 14 files changed, 387 insertions(+), 159 deletions(-) diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 2e20cdc81..b6293993c 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -23,7 +23,6 @@ http-body = { version = "0.4.5", optional = true } # axum axum = { version = "0.6.1", features = ["ws"], optional = true } tower-http = { version = "0.4.0", optional = true, features = ["fs"] } -hyper = { version = "0.14.25", optional = true } axum-macros = "0.3.7" # salvo @@ -33,6 +32,8 @@ serde = "1.0.159" # Dioxus + SSR dioxus-core = { path = "../core", version = "^0.3.0" } dioxus-ssr = { path = "../ssr", version = "^0.3.0", optional = true } +hyper = { version = "0.14.25", optional = true } +http = { version = "0.2.9", optional = true } log = "0.4.17" once_cell = "1.17.1" @@ -57,6 +58,6 @@ web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "Ht default = ["hot-reload"] hot-reload = ["serde_json", "tokio-stream", "futures-util"] warp = ["dep:warp", "http-body", "ssr"] -axum = ["dep:axum", "tower-http", "hyper", "ssr"] -salvo = ["dep:salvo", "hyper", "ssr"] -ssr = ["server_fn/ssr", "tokio", "dioxus-ssr"] +axum = ["dep:axum", "tower-http", "ssr"] +salvo = ["dep:salvo", "ssr"] +ssr = ["server_fn/ssr", "tokio", "dioxus-ssr", "hyper", "http"] diff --git a/packages/server/examples/axum-hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml index fd1f051d1..b2c14561d 100644 --- a/packages/server/examples/axum-hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -18,3 +18,10 @@ serde = "1.0.159" default = ["web"] ssr = ["axum", "tokio", "dioxus-server/axum"] web = ["dioxus-web"] + +[profile.release] +lto = true +panic = "abort" +opt-level = 3 +strip = true +codegen-units = 1 diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index 5b3323ec4..6b6f32db7 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -56,11 +56,12 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| { to_owned![text]; + let sc = cx.sc(); async move { if let Ok(data) = get_server_data().await { println!("Client received: {}", data); text.set(data.clone()); - post_server_data(data).await.unwrap(); + post_server_data(sc, data).await.unwrap(); } } }, @@ -71,8 +72,12 @@ fn app(cx: Scope) -> Element { } #[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +async fn post_server_data(cx: DioxusServerContext, data: String) -> Result<(), ServerFnError> { + // The server context contains information about the current request and allows you to modify the response. + cx.responce_headers_mut() + .insert("Set-Cookie", "foo=bar".parse().unwrap()); println!("Server received: {}", data); + println!("Request parts are {:?}", cx.request_parts()); Ok(()) } diff --git a/packages/server/examples/axum-router/Cargo.toml b/packages/server/examples/axum-router/Cargo.toml index 699779763..3e137d8f6 100644 --- a/packages/server/examples/axum-router/Cargo.toml +++ b/packages/server/examples/axum-router/Cargo.toml @@ -21,3 +21,10 @@ http = { version = "0.2.9", optional = true } default = ["web"] ssr = ["axum", "tokio", "dioxus-server/axum", "tower-http", "http"] web = ["dioxus-web", "dioxus-router/web"] + +[profile.release] +lto = true +panic = "abort" +opt-level = 3 +strip = true +codegen-units = 1 diff --git a/packages/server/examples/salvo-hello-world/Cargo.toml b/packages/server/examples/salvo-hello-world/Cargo.toml index 1ddf52915..9146ce3e2 100644 --- a/packages/server/examples/salvo-hello-world/Cargo.toml +++ b/packages/server/examples/salvo-hello-world/Cargo.toml @@ -18,3 +18,10 @@ salvo = { version = "0.37.9", optional = true } default = ["web"] ssr = ["salvo", "tokio", "dioxus-server/salvo"] web = ["dioxus-web"] + +[profile.release] +lto = true +panic = "abort" +opt-level = 3 +strip = true +codegen-units = 1 diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs index a6e33dc63..ff1081077 100644 --- a/packages/server/examples/salvo-hello-world/src/main.rs +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -44,6 +44,7 @@ struct AppProps { fn app(cx: Scope) -> Element { let mut count = use_state(cx, || cx.props.count); let text = use_state(cx, || "...".to_string()); + let server_context = cx.sc(); cx.render(rsx! { h1 { "High-Five counter: {count}" } @@ -51,12 +52,12 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| { - to_owned![text]; + to_owned![text, server_context]; async move { if let Ok(data) = get_server_data().await { println!("Client received: {}", data); text.set(data.clone()); - post_server_data(data).await.unwrap(); + post_server_data(server_context, data).await.unwrap(); } } }, @@ -67,8 +68,12 @@ fn app(cx: Scope) -> Element { } #[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +async fn post_server_data(cx: DioxusServerContext, data: String) -> Result<(), ServerFnError> { + // The server context contains information about the current request and allows you to modify the response. + cx.responce_headers_mut() + .insert("Set-Cookie", "foo=bar".parse().unwrap()); println!("Server received: {}", data); + println!("Request parts are {:?}", cx.request_parts()); Ok(()) } diff --git a/packages/server/examples/warp-hello-world/Cargo.toml b/packages/server/examples/warp-hello-world/Cargo.toml index cbd8606dd..e182494b0 100644 --- a/packages/server/examples/warp-hello-world/Cargo.toml +++ b/packages/server/examples/warp-hello-world/Cargo.toml @@ -18,3 +18,10 @@ warp = { version = "0.3.3", optional = true } default = ["web"] ssr = ["warp", "tokio", "dioxus-server/warp"] web = ["dioxus-web"] + +[profile.release] +lto = true +panic = "abort" +opt-level = 3 +strip = true +codegen-units = 1 diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs index 4929af0c8..04684f803 100644 --- a/packages/server/examples/warp-hello-world/src/main.rs +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -41,6 +41,7 @@ struct AppProps { fn app(cx: Scope) -> Element { let mut count = use_state(cx, || cx.props.count); let text = use_state(cx, || "...".to_string()); + let server_context = cx.sc(); cx.render(rsx! { h1 { "High-Five counter: {count}" } @@ -48,12 +49,12 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| { - to_owned![text]; + to_owned![text, server_context]; async move { if let Ok(data) = get_server_data().await { println!("Client received: {}", data); text.set(data.clone()); - post_server_data(data).await.unwrap(); + post_server_data(server_context, data).await.unwrap(); } } }, @@ -64,8 +65,12 @@ fn app(cx: Scope) -> Element { } #[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +async fn post_server_data(cx: DioxusServerContext, data: String) -> Result<(), ServerFnError> { + // The server context contains information about the current request and allows you to modify the response. + cx.responce_headers_mut() + .insert("Set-Cookie", "foo=bar".parse().unwrap()); println!("Server received: {}", data); + println!("Request parts are {:?}", cx.request_parts()); Ok(()) } diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 7fc47a8b3..1b572c804 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -57,16 +57,17 @@ use axum::{ body::{self, Body, BoxBody, Full}, - extract::RawQuery, extract::{State, WebSocketUpgrade}, handler::Handler, - http::{HeaderMap, Request, Response, StatusCode}, + http::{Request, Response, StatusCode}, response::IntoResponse, routing::{get, post}, Router, }; +use dioxus_core::VirtualDom; use server_fn::{Encoding, Payload, ServerFunctionRegistry}; use std::error::Error; +use std::sync::Arc; use tokio::task::spawn_blocking; use crate::{ @@ -223,8 +224,11 @@ where fn register_server_fns(self, server_fn_route: &'static str) -> Self { self.register_server_fns_with_handler(server_fn_route, |func| { - move |headers: HeaderMap, RawQuery(query): RawQuery, body: Request| async move { - server_fn_handler((), func.clone(), headers, query, body).await + move |req: Request| async move { + let (parts, body) = req.into_parts(); + let parts: Arc = Arc::new(parts.into()); + let server_context = DioxusServerContext::new(parts.clone()); + server_fn_handler(server_context, func.clone(), parts, body).await } }) } @@ -296,44 +300,54 @@ where async fn render_handler( State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, + request: Request, ) -> impl IntoResponse { - let rendered = ssr_state.render(&cfg); + let (parts, _) = request.into_parts(); + let parts: Arc = Arc::new(parts.into()); + let server_context = DioxusServerContext::new(parts); + let mut vdom = + VirtualDom::new_with_props(cfg.app, cfg.props.clone()).with_root_context(server_context); + let _ = vdom.rebuild(); + + let rendered = ssr_state.render_vdom(&vdom, &cfg); Full::from(rendered) } /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( - server_context: impl Into, + server_context: DioxusServerContext, function: ServerFunction, - headers: HeaderMap, - query: Option, - req: Request, + parts: Arc, + body: Body, ) -> impl IntoResponse { - let server_context = server_context.into(); - let (_, body) = req.into_parts(); let body = hyper::body::to_bytes(body).await; - let Ok(body)=body else { + let Ok(body) = body else { return report_err(body.err().unwrap()); }; // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); + let query_string = parts.uri.query().unwrap_or_default().to_string(); spawn_blocking({ move || { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async { - let query = &query.unwrap_or_default().into(); + let query = &query_string.into(); let data = match &function.encoding { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => query, }; - let resp = match (function.trait_obj)(server_context, &data).await { + let resp = match (function.trait_obj)(server_context.clone(), &data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response - let accept_header = - headers.get("Accept").and_then(|value| value.to_str().ok()); + let accept_header = parts + .headers + .get("Accept") + .and_then(|value| value.to_str().ok()); let mut res = Response::builder(); + *res.headers_mut().expect("empty responce should be valid") = + server_context.take_responce_headers(); if accept_header == Some("application/json") || accept_header == Some( diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index ca11ff9ec..6ed4c2b3e 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -50,8 +50,7 @@ //! } //! ``` -use std::error::Error; - +use dioxus_core::VirtualDom; use hyper::{http::HeaderValue, StatusCode}; use salvo::{ async_trait, handler, @@ -59,6 +58,8 @@ use salvo::{ Depot, FlowCtrl, Handler, Request, Response, Router, }; use server_fn::{Encoding, Payload, ServerFunctionRegistry}; +use std::error::Error; +use std::sync::Arc; use tokio::task::spawn_blocking; use crate::{ @@ -172,7 +173,7 @@ pub trait DioxusRouterExt { /// } /// /// fn app(cx: Scope) -> Element {todo!()} - /// ``` + /// ``` fn serve_dioxus_application( self, server_fn_path: &'static str, @@ -264,6 +265,17 @@ impl DioxusRouterExt for Router { } } +/// Extracts the parts of a request that are needed for server functions. This will take parts of the request and replace them with empty values. +pub fn extract_parts(req: &mut Request) -> RequestParts { + RequestParts { + method: std::mem::take(req.method_mut()), + uri: std::mem::take(req.uri_mut()), + version: req.version(), + headers: std::mem::take(req.headers_mut()), + extensions: std::mem::take(req.extensions_mut()), + } +} + struct SSRHandler { cfg: ServeConfig

, } @@ -272,7 +284,7 @@ struct SSRHandler { impl Handler for SSRHandler

{ async fn handle( &self, - _req: &mut Request, + req: &mut Request, depot: &mut Depot, res: &mut Response, _flow: &mut FlowCtrl, @@ -285,7 +297,16 @@ impl Handler for SSRHandler depot.inject(renderer.clone()); renderer }; - res.write_body(renderer_pool.render(&self.cfg)).unwrap(); + let parts: Arc = Arc::new(extract_parts(req)); + let server_context = DioxusServerContext::new(parts); + let mut vdom = VirtualDom::new_with_props(self.cfg.app, self.cfg.props.clone()) + .with_root_context(server_context.clone()); + let _ = vdom.rebuild(); + + res.write_body(renderer_pool.render_vdom(&vdom, &self.cfg)) + .unwrap(); + + *res.headers_mut() = server_context.take_responce_headers(); } } @@ -314,17 +335,6 @@ impl ServerFnHandler { function, } = self; - let body = hyper::body::to_bytes(req.body_mut().unwrap()).await; - let Ok(body)=body else { - handle_error(body.err().unwrap(), res); - return; - }; - let headers = req.headers(); - - // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime - let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); - let function = function.clone(); - let server_context = server_context.clone(); let query = req .uri() .query() @@ -332,7 +342,22 @@ impl ServerFnHandler { .as_bytes() .to_vec() .into(); + let body = hyper::body::to_bytes(req.body_mut().unwrap()).await; + let Ok(body)=body else { + handle_error(body.err().unwrap(), res); + return; + }; + let headers = req.headers(); + let accept_header = headers.get("Accept").cloned(); + + let parts = Arc::new(extract_parts(req)); + + // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime + let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); spawn_blocking({ + let function = function.clone(); + let mut server_context = server_context.clone(); + server_context.parts = parts; move || { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") @@ -349,10 +374,13 @@ impl ServerFnHandler { }); let result = resp_rx.await.unwrap(); + // Set the headers from the server context + *res.headers_mut() = server_context.take_responce_headers(); + match result { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response - let accept_header = headers.get("Accept").and_then(|value| value.to_str().ok()); + let accept_header = accept_header.as_ref().and_then(|value| value.to_str().ok()); if accept_header == Some("application/json") || accept_header == Some( diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 414160045..31d4daba4 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -51,13 +51,16 @@ use crate::{ prelude::*, render::SSRState, serve_config::ServeConfig, server_fn::DioxusServerFnRegistry, }; +use dioxus_core::VirtualDom; use server_fn::{Encoding, Payload, ServerFunctionRegistry}; use std::error::Error; +use std::sync::Arc; use tokio::task::spawn_blocking; +use warp::path::FullPath; use warp::{ filters::BoxedFilter, http::{Response, StatusCode}, - hyper::{body::Bytes, HeaderMap}, + hyper::body::Bytes, path, Filter, Reply, }; @@ -125,48 +128,16 @@ where /// ``` pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { register_server_fns_with_handler(server_fn_route, |full_route, func| { - let func2 = func.clone(); - let func3 = func.clone(); path(full_route.clone()) - .and(warp::filters::method::get()) - .and(warp::header::headers_cloned()) - .and(warp::filters::query::raw()) + .and(warp::post().or(warp::get()).unify()) + .and(request_parts()) .and(warp::body::bytes()) - .and_then(move |headers, query, body| { + .and_then(move |parts, bytes| { let func = func.clone(); async move { - server_fn_handler( - DioxusServerContext::default(), - func, - headers, - Some(query), - body, - ) - .await + server_fn_handler(DioxusServerContext::default(), func, parts, bytes).await } }) - .or(path(full_route.clone()) - .and(warp::filters::method::get()) - .and(warp::header::headers_cloned()) - .and(warp::body::bytes()) - .and_then(move |headers, body| { - let func = func2.clone(); - async move { - server_fn_handler(DioxusServerContext::default(), func, headers, None, body) - .await - } - })) - .or(path(full_route) - .and(warp::filters::method::post()) - .and(warp::header::headers_cloned()) - .and(warp::body::bytes()) - .and_then(move |headers, body| { - let func = func3.clone(); - async move { - server_fn_handler(DioxusServerContext::default(), func, headers, None, body) - .await - } - })) }) } @@ -199,18 +170,71 @@ pub fn serve_dioxus_application( + cfg: ServeConfig

, +) -> impl Filter + Clone { + warp::get() + .and(request_parts()) + .and(with_ssr_state()) + .map(move |parts, renderer: SSRState| { + let parts = Arc::new(parts); + + let server_context = DioxusServerContext::new(parts); + + let mut vdom = VirtualDom::new_with_props(cfg.app, cfg.props.clone()) + .with_root_context(server_context.clone()); + let _ = vdom.rebuild(); + + let html = renderer.render_vdom(&vdom, &cfg); + + let mut res = Response::builder(); + + *res.headers_mut().expect("empty request should be valid") = + server_context.take_responce_headers(); + + res.header("Content-Type", "text/html") + .body(Bytes::from(html)) + .unwrap() + }) +} + +/// An extractor for the request parts (used in [DioxusServerContext]). This will extract the method, uri, query, and headers from the request. +pub fn request_parts( +) -> impl Filter + Clone { + warp::method() + .and(warp::filters::path::full()) + .and( + warp::filters::query::raw() + .or(warp::any().map(String::new)) + .unify(), + ) + .and(warp::header::headers_cloned()) + .and_then(move |method, path: FullPath, query, headers| async move { + http::uri::Builder::new() + .path_and_query(format!("{}?{}", path.as_str(), query)) + .build() + .map_err(|err| { + warp::reject::custom(FailedToReadBody(format!("Failed to build uri: {}", err))) + }) + .map(|uri| RequestParts { + method, + uri, + headers, + ..Default::default() + }) + }) +} + fn with_ssr_state() -> impl Filter + Clone { - let renderer = SSRState::default(); - warp::any().map(move || renderer.clone()) + let state = SSRState::default(); + warp::any().map(move || state.clone()) } #[derive(Debug)] @@ -227,29 +251,46 @@ impl warp::reject::Reject for RecieveFailed {} pub async fn server_fn_handler( server_context: impl Into, function: ServerFunction, - headers: HeaderMap, - query: Option, + parts: RequestParts, body: Bytes, ) -> Result, warp::Rejection> { - let server_context = server_context.into(); + let mut server_context = server_context.into(); + + let parts = Arc::new(parts); + + server_context.parts = parts.clone(); + // Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime let (resp_tx, resp_rx) = tokio::sync::oneshot::channel(); spawn_blocking({ move || { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") - .block_on(async { - let query = &query.unwrap_or_default().into(); + .block_on(async move { + let query = parts + .uri + .query() + .unwrap_or_default() + .as_bytes() + .to_vec() + .into(); let data = match &function.encoding { Encoding::Url | Encoding::Cbor => &body, - Encoding::GetJSON | Encoding::GetCBOR => query, + Encoding::GetJSON | Encoding::GetCBOR => &query, }; - let resp = match (function.trait_obj)(server_context, &data).await { + let resp = match (function.trait_obj)(server_context.clone(), data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response - let accept_header = - headers.get("Accept").and_then(|value| value.to_str().ok()); + let accept_header = parts + .headers + .get("Accept") + .as_ref() + .and_then(|value| value.to_str().ok()); let mut res = Response::builder(); + + *res.headers_mut().expect("empty request should be valid") = + server_context.take_responce_headers(); + if accept_header == Some("application/json") || accept_header == Some( diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index c5ad196bc..8d18dca2a 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -30,8 +30,10 @@ pub mod prelude { #[cfg(feature = "ssr")] pub use crate::render::SSRState; #[cfg(feature = "ssr")] - pub use crate::serve_config::{ServeConfig, ServeConfigBuilder}; - pub use crate::server_context::DioxusServerContext; + pub use crate::serve_config::{ ServeConfig, ServeConfigBuilder}; + #[cfg(feature = "ssr")] + pub use crate::server_context::{RequestParts}; + pub use crate::server_context::{ DioxusServerContext, HasServerContext}; pub use crate::server_fn::ServerFn; #[cfg(feature = "ssr")] pub use crate::server_fn::{ServerFnTraitObj, ServerFunction}; diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 0f61a3a0f..e8f6a67ec 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -26,20 +26,31 @@ impl SSRState { /// Render the application to HTML. pub fn render(&self, cfg: &ServeConfig

) -> String { let ServeConfig { - app, props, index, .. + app, props, .. } = cfg; let mut vdom = VirtualDom::new_with_props(*app, props.clone()); let _ = vdom.rebuild(); + self.render_vdom(&vdom, cfg) + } + + /// Render a VirtualDom to HTML. + pub fn render_vdom( + &self, + vdom: &VirtualDom, + cfg: &ServeConfig

, + ) -> String { + let ServeConfig { index, .. } = cfg; + let mut renderer = self.renderers.pull(pre_renderer); let mut html = String::new(); html += &index.pre_main; - let _ = renderer.render_to(&mut html, &vdom); + let _ = renderer.render_to(&mut html, vdom); // serialize the props let _ = crate::props_html::serialize_props::encode_in_element(&cfg.props, &mut html); diff --git a/packages/server/src/server_context.rs b/packages/server/src/server_context.rs index 7e2a494c5..aff9ede35 100644 --- a/packages/server/src/server_context.rs +++ b/packages/server/src/server_context.rs @@ -1,72 +1,160 @@ -use std::sync::{Arc, PoisonError, RwLock, RwLockWriteGuard}; +use dioxus_core::ScopeState; -use anymap::{any::Any, Map}; +/// A trait for an object that contains a server context +pub trait HasServerContext { + /// Get the server context from the state + fn server_context(&self) -> DioxusServerContext; -type SendSyncAnyMap = Map; - -/// A shared context for server functions. -/// This allows you to pass data between your server framework and the server functions. This can be used to pass request information or information about the state of the server. For example, you could pass authentication data though this context to your server functions. -#[derive(Clone)] -pub struct DioxusServerContext { - shared_context: Arc>, + /// A shortcut for `self.server_context()` + fn sc(&self) -> DioxusServerContext { + self.server_context() + } } -impl Default for DioxusServerContext { - fn default() -> Self { - Self { - shared_context: Arc::new(RwLock::new(SendSyncAnyMap::new())), +impl HasServerContext for &ScopeState { + fn server_context(&self) -> DioxusServerContext { + #[cfg(feature = "ssr")] + { + self.consume_context().expect("No server context found") + } + #[cfg(not(feature = "ssr"))] + { + DioxusServerContext {} } } } -impl DioxusServerContext { - /// Clone a value from the shared server context - pub fn get(&self) -> Option { - self.shared_context.read().ok()?.get::().cloned() - } +/// A shared context for server functions that contains infomation about the request and middleware state. +/// This allows you to pass data between your server framework and the server functions. This can be used to pass request information or information about the state of the server. For example, you could pass authentication data though this context to your server functions. +/// +/// You should not construct this directly inside components. Instead use the `HasServerContext` trait to get the server context from the scope. +#[derive(Clone)] +pub struct DioxusServerContext { + #[cfg(feature = "ssr")] + shared_context: std::sync::Arc< + std::sync::RwLock>, + >, + #[cfg(feature = "ssr")] + headers: std::sync::Arc>, + #[cfg(feature = "ssr")] + pub(crate) parts: std::sync::Arc, +} - /// Insert a value into the shared server context - pub fn insert( - &mut self, - value: T, - ) -> Result<(), PoisonError>> { - self.shared_context - .write() - .map(|mut map| map.insert(value)) - .map(|_| ()) +#[allow(clippy::derivable_impls)] +impl Default for DioxusServerContext { + fn default() -> Self { + Self { + #[cfg(feature = "ssr")] + shared_context: std::sync::Arc::new(std::sync::RwLock::new(anymap::Map::new())), + #[cfg(feature = "ssr")] + headers: Default::default(), + #[cfg(feature = "ssr")] + parts: Default::default(), + } } } -/// Generate a server context from a tuple of values -macro_rules! server_context { - ($({$(($name:ident: $ty:ident)),*}),*) => { - $( - #[allow(unused_mut)] - impl< $($ty: Send + Sync + 'static),* > From<($($ty,)*)> for $crate::server_context::DioxusServerContext { - fn from(( $($name,)* ): ($($ty,)*)) -> Self { - let mut context = $crate::server_context::DioxusServerContext::default(); - $(context.insert::<$ty>($name).unwrap();)* - context - } +#[cfg(feature = "ssr")] +pub use server_fn_impl::*; + +#[cfg(feature = "ssr")] +mod server_fn_impl { + use super::*; + use std::sync::LockResult; + use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; + + use anymap::{any::Any, Map}; + type SendSyncAnyMap = Map; + + impl DioxusServerContext { + /// Create a new server context from a request + pub fn new(parts: impl Into>) -> Self { + Self { + parts: parts.into(), + shared_context: Arc::new(RwLock::new(SendSyncAnyMap::new())), + headers: Default::default(), } - )* - }; -} + } -server_context!( - {}, - {(a: A)}, - {(a: A), (b: B)}, - {(a: A), (b: B), (c: C)}, - {(a: A), (b: B), (c: C), (d: D)}, - {(a: A), (b: B), (c: C), (d: D), (e: E)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K), (l: L)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K), (l: L), (m: M)}, - {(a: A), (b: B), (c: C), (d: D), (e: E), (f: F), (g: G), (h: H), (i: I), (j: J), (k: K), (l: L), (m: M), (n: N)} -); + /// Clone a value from the shared server context + pub fn get(&self) -> Option { + self.shared_context.read().ok()?.get::().cloned() + } + + /// Insert a value into the shared server context + pub fn insert( + &mut self, + value: T, + ) -> Result<(), PoisonError>> { + self.shared_context + .write() + .map(|mut map| map.insert(value)) + .map(|_| ()) + } + + /// Get the headers from the server context + pub fn responce_headers(&self) -> RwLockReadGuard<'_, hyper::header::HeaderMap> { + self.try_responce_headers() + .expect("Failed to get headers from server context") + } + + /// Try to get the headers from the server context + pub fn try_responce_headers( + &self, + ) -> LockResult> { + self.headers.read() + } + + /// Get the headers mutably from the server context + pub fn responce_headers_mut(&self) -> RwLockWriteGuard<'_, hyper::header::HeaderMap> { + self.try_responce_headers_mut() + .expect("Failed to get headers mutably from server context") + } + + /// Try to get the headers mut from the server context + pub fn try_responce_headers_mut( + &self, + ) -> LockResult> { + self.headers.write() + } + + pub(crate) fn take_responce_headers(&self) -> hyper::header::HeaderMap { + let mut headers = self.headers.write().unwrap(); + std::mem::take(&mut *headers) + } + + /// Get the request that triggered: + /// - The initial SSR render if called from a ScopeState or ServerFn + /// - The server function to be called if called from a server function after the initial render + pub fn request_parts(&self) -> &RequestParts { + &self.parts + } + } + + /// Associated parts of an HTTP Request + #[derive(Debug, Default)] + pub struct RequestParts { + /// The request's method + pub method: http::Method, + /// The request's URI + pub uri: http::Uri, + /// The request's version + pub version: http::Version, + /// The request's headers + pub headers: http::HeaderMap, + /// The request's extensions + pub extensions: http::Extensions, + } + + impl From for RequestParts { + fn from(parts: http::request::Parts) -> Self { + Self { + method: parts.method, + uri: parts.uri, + version: parts.version, + headers: parts.headers, + extensions: parts.extensions, + } + } + } +} From 64c7fda201fa718ca2d80c83243d6b537a8e8b53 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 29 Apr 2023 17:32:16 -0500 Subject: [PATCH 390/516] fix warp release builds --- packages/server/src/adapters/warp_adapter.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 31d4daba4..5e9d23562 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -360,12 +360,10 @@ pub fn connect_hot_reload() -> impl Filter Date: Sat, 29 Apr 2023 20:42:47 -0500 Subject: [PATCH 391/516] Remove console.log --- packages/interpreter/src/sledgehammer_bindings.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index a15d7bc81..2cb148e89 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -118,7 +118,6 @@ mod js { nodes[id] = node; } export function get_node(id) { - console.log(nodes, id); return nodes[id]; } export function initilize(root, handler) { From f6cd48c6123204c4979b154516d46876ec61d59a Mon Sep 17 00:00:00 2001 From: Antonio Curavalea Date: Sun, 30 Apr 2023 23:13:46 +0300 Subject: [PATCH 392/516] feat: open browser during serve with "--open" --- Cargo.lock | 36 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/cli/cfg.rs | 5 +++++ src/cli/serve/mod.rs | 2 +- src/server/mod.rs | 39 ++++++++++++++++++++++++++++----------- 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41d9f8eb5..abf1ce3a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,6 +722,7 @@ dependencies = [ "log", "mlua", "notify", + "open", "proc-macro2", "regex", "reqwest", @@ -1521,6 +1522,25 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_executable" version = "0.1.2" @@ -1898,6 +1918,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16814a067484415fda653868c9be0ac5f2abd2ef5d951082a5f2fe1b3662944" +dependencies = [ + "is-wsl", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.45" @@ -1992,6 +2022,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 45539252c..9219db5ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ dioxus-html = { version = "0.3", features = ["hot-reload-context"] } dioxus-core = { version = "0.3", features = ["serialize"] } dioxus-autofmt = "0.3.0" rsx-rosetta = { version = "0.3" } +open = "4.1.0" [[bin]] path = "src/main.rs" diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 9fe453c49..ee6284ac1 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -45,6 +45,11 @@ pub struct ConfigOptsServe { #[clap(default_value_t = 8080)] pub port: u16, + /// Open the app in the default browser [default: false] + #[clap(long)] + #[serde(default)] + pub open: bool, + /// Build a example [default: ""] #[clap(long)] pub example: Option, diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index ce0dd743e..e3485c1f1 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -68,7 +68,7 @@ impl Serve { Serve::regen_dev_page(&crate_config)?; // start the develop server - server::startup(self.serve.port, crate_config.clone()).await?; + server::startup(self.serve.port, crate_config.clone(), self.serve.open).await?; Ok(()) } diff --git a/src/server/mod.rs b/src/server/mod.rs index 42622466b..6daf7e289 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -55,7 +55,7 @@ struct WsReloadState { update: broadcast::Sender<()>, } -pub async fn startup(port: u16, config: CrateConfig) -> Result<()> { +pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Result<()> { // ctrl-c shutdown checker let crate_config = config.clone(); let _ = ctrlc::set_handler(move || { @@ -64,10 +64,11 @@ pub async fn startup(port: u16, config: CrateConfig) -> Result<()> { }); let ip = get_ip().unwrap_or(String::from("0.0.0.0")); + if config.hot_reload { - startup_hot_reload(ip, port, config).await? + startup_hot_reload(ip, port, config, start_browser).await? } else { - startup_default(ip, port, config).await? + startup_default(ip, port, config, start_browser).await? } Ok(()) } @@ -129,7 +130,7 @@ pub async fn hot_reload_handler( } #[allow(unused_assignments)] -pub async fn startup_hot_reload(ip: String, port: u16, config: CrateConfig) -> Result<()> { +pub async fn startup_hot_reload(ip: String, port: u16, config: CrateConfig, start_browser: bool) -> Result<()> { let first_build_result = crate::builder::build(&config, false)?; log::info!("🚀 Starting development server..."); @@ -319,14 +320,25 @@ pub async fn startup_hot_reload(ip: String, port: u16, config: CrateConfig) -> R .layer(Extension(ws_reload_state)) .layer(Extension(hot_reload_state)); - axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) - .serve(router.into_make_service()) - .await?; + let addr = format!("0.0.0.0:{}", port).parse().unwrap(); + + let server = axum::Server::bind(&addr).serve(router.into_make_service()); + + if start_browser { + let _ = open::that(format!("http://{}", addr)); + } + + server.await?; Ok(()) } -pub async fn startup_default(ip: String, port: u16, config: CrateConfig) -> Result<()> { +pub async fn startup_default( + ip: String, + port: u16, + config: CrateConfig, + start_browser: bool, +) -> Result<()> { let first_build_result = crate::builder::build(&config, false)?; log::info!("🚀 Starting development server..."); @@ -461,9 +473,14 @@ pub async fn startup_default(ip: String, port: u16, config: CrateConfig) -> Resu ) .layer(Extension(ws_reload_state)); - axum::Server::bind(&format!("0.0.0.0:{}", port).parse().unwrap()) - .serve(router.into_make_service()) - .await?; + let addr = format!("0.0.0.0:{}", port).parse().unwrap(); + let server = axum::Server::bind(&addr).serve(router.into_make_service()); + + if start_browser { + let _ = open::that(format!("http://{}", addr)); + } + + server.await?; Ok(()) } From 35dcacb9562569434cc5e6f95cefbb95904d26da Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 30 Apr 2023 16:35:11 -0500 Subject: [PATCH 393/516] use ascii64 encoding --- packages/server/Cargo.toml | 1 + .../src/props_html/deserialize_props.rs | 13 ++------ packages/server/src/props_html/mod.rs | 31 ------------------- .../server/src/props_html/serialize_props.rs | 12 ++----- 4 files changed, 7 insertions(+), 50 deletions(-) diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index b6293993c..753d4c2cb 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -47,6 +47,7 @@ tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } futures-util = { version = "0.3.28", optional = true } postcard = { version = "1.0.4", features = ["use-std"] } yazi = "0.1.5" +base64 = "0.21.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] dioxus-hot-reload = { path = "../hot-reload" } diff --git a/packages/server/src/props_html/deserialize_props.rs b/packages/server/src/props_html/deserialize_props.rs index 64f2181ab..dcaa19905 100644 --- a/packages/server/src/props_html/deserialize_props.rs +++ b/packages/server/src/props_html/deserialize_props.rs @@ -1,18 +1,11 @@ use serde::de::DeserializeOwned; -use super::u16_from_char; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; #[allow(unused)] pub(crate) fn serde_from_string(string: &str) -> Option { - let decompressed = string - .chars() - .flat_map(|c| { - let u = u16_from_char(c); - let u1 = (u >> 8) as u8; - let u2 = (u & 0xFF) as u8; - [u1, u2].into_iter() - }) - .collect::>(); + let decompressed = STANDARD.decode(string.as_bytes()).ok()?; let (decompressed, _) = yazi::decompress(&decompressed, yazi::Format::Zlib).unwrap(); postcard::from_bytes(&decompressed).ok() diff --git a/packages/server/src/props_html/mod.rs b/packages/server/src/props_html/mod.rs index 9e9b8721e..048e2aa90 100644 --- a/packages/server/src/props_html/mod.rs +++ b/packages/server/src/props_html/mod.rs @@ -52,34 +52,3 @@ fn serialized_and_deserializes() { } } } - -#[test] -fn encodes_and_decodes_bytes() { - for i in 0..(u16::MAX) { - let c = u16_to_char(i); - let i2 = u16_from_char(c); - assert_eq!(i, i2); - } -} - -#[allow(unused)] -pub(crate) fn u16_to_char(u: u16) -> char { - let u = u as u32; - let mapped = if u <= 0xD7FF { - u - } else { - 0xE000 + (u - 0xD7FF) - }; - char::from_u32(mapped).unwrap() -} - -#[allow(unused)] -pub(crate) fn u16_from_char(c: char) -> u16 { - let c = c as u32; - let mapped = if c <= 0xD7FF { - c - } else { - 0xD7FF + (c - 0xE000) - }; - mapped as u16 -} diff --git a/packages/server/src/props_html/serialize_props.rs b/packages/server/src/props_html/serialize_props.rs index f1282f07f..0012afb9c 100644 --- a/packages/server/src/props_html/serialize_props.rs +++ b/packages/server/src/props_html/serialize_props.rs @@ -1,6 +1,7 @@ use serde::Serialize; -use super::u16_to_char; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; #[allow(unused)] pub(crate) fn serde_to_writable( @@ -14,14 +15,7 @@ pub(crate) fn serde_to_writable( yazi::CompressionLevel::BestSize, ) .unwrap(); - for array in compressed.chunks(2) { - let w = if array.len() == 2 { - [array[0], array[1]] - } else { - [array[0], 0] - }; - write_to.write_char(u16_to_char((w[0] as u16) << 8 | (w[1] as u16)))?; - } + write_to.write_str(&STANDARD.encode(compressed)); Ok(()) } From 970d9937580e4c2f2e2c1d5ac982442714da17c9 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 1 May 2023 11:27:19 -0500 Subject: [PATCH 394/516] attempt to reconnect to server after sever disconnects in debug mode --- .../server/examples/axum-router/src/main.rs | 3 +- packages/server/src/adapters/axum_adapter.rs | 43 +++++++++++++++---- packages/server/src/adapters/salvo_adapter.rs | 25 ++++++++++- packages/server/src/adapters/warp_adapter.rs | 33 +++++++++++--- packages/server/src/render.rs | 39 +++++++++++++++-- 5 files changed, 122 insertions(+), 21 deletions(-) diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs index 2afa4a481..3994eeb51 100644 --- a/packages/server/examples/axum-router/src/main.rs +++ b/packages/server/examples/axum-router/src/main.rs @@ -9,6 +9,7 @@ use dioxus::prelude::*; use dioxus_router::*; use dioxus_server::prelude::*; +use serde::{Deserialize, Serialize}; fn main() { #[cfg(feature = "web")] @@ -62,7 +63,7 @@ fn main() { } } -#[derive(Clone, Debug, Props, PartialEq)] +#[derive(Clone, Debug, Props, PartialEq, Serialize, Deserialize)] struct AppProps { route: Option, } diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 1b572c804..541094674 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -267,6 +267,7 @@ where .collect::>() .join("/"); let route = format!("/{}", route); + println!("Serving static asset at {}", route); if path.is_dir() { self = self.nest_service(&route, ServeDir::new(path)); } else { @@ -275,20 +276,43 @@ where } // Add server functions and render index.html - self.connect_hot_reload() - .register_server_fns(server_fn_route) - .route( - "/", - get(render_handler).with_state((cfg, SSRState::default())), - ) + self.route( + "/", + get(render_handler).with_state((cfg, SSRState::default())), + ) + .connect_hot_reload() + .register_server_fns(server_fn_route) } fn connect_hot_reload(self) -> Self { #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] { - self.route( - "/_dioxus/hot_reload", - get(hot_reload_handler).with_state(crate::hot_reload::HotReloadState::default()), + self.nest( + "/_dioxus", + Router::new() + .route( + "/hot_reload", + get(hot_reload_handler) + .with_state(crate::hot_reload::HotReloadState::default()), + ) + .route( + "/disconnect", + get(|ws: WebSocketUpgrade| async { + ws.on_failed_upgrade(|error| { + println!("failed to upgrade: {}", error); + todo!() + }) + .on_upgrade(|mut ws| async move { + use axum::extract::ws::Message; + let _ = ws.send(Message::Text("connected".into())).await; + loop { + if ws.recv().await.is_none() { + break; + } + } + }) + }), + ), ) } #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] @@ -302,6 +326,7 @@ async fn render_handler( State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, request: Request, ) -> impl IntoResponse { + println!("Rendering"); let (parts, _) = request.into_parts(); let parts: Arc = Arc::new(parts.into()); let server_context = DioxusServerContext::new(parts); diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 6ed4c2b3e..3751211e4 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -261,7 +261,14 @@ impl DioxusRouterExt for Router { } fn connect_hot_reload(self) -> Self { - self.push(Router::with_path("/_dioxus/hot_reload").get(HotReloadHandler::default())) + let mut _dioxus_router = Router::with_path("_dioxus"); + _dioxus_router = _dioxus_router + .push(Router::with_path("hot_reload").handle(HotReloadHandler::default())); + #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] + { + _dioxus_router = _dioxus_router.push(Router::with_path("disconnect").handle(ignore_ws)); + } + self.push(_dioxus_router) } } @@ -504,3 +511,19 @@ impl HotReloadHandler { .await } } + +#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] +#[handler] +async fn ignore_ws(req: &mut Request, res: &mut Response) -> Result<(), salvo::http::StatusError> { + use salvo::ws::WebSocketUpgrade; + WebSocketUpgrade::new() + .upgrade(req, res, |mut ws| async move { + let _ = dbg!(ws.send(salvo::ws::Message::text("connected")).await); + while let Some(msg) = ws.recv().await { + if msg.is_err() { + return; + }; + } + }) + .await +} diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 5e9d23562..3d41d76cf 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -360,7 +360,7 @@ pub fn connect_hot_reload() -> impl Filter impl Filter impl Filter); + impl Drop for DisconnectOnDrop { + fn drop(&mut self) { + let _ = self.0.take().unwrap().close(); + } + } + + let _ = websocket.send(Message::text("connected")).await; + let mut ws = DisconnectOnDrop(Some(websocket)); + + loop { + ws.0.as_mut().unwrap().next().await; + } + }) + }, + )) } } diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index e8f6a67ec..1d32e2acd 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -25,9 +25,7 @@ impl Default for SSRState { impl SSRState { /// Render the application to HTML. pub fn render(&self, cfg: &ServeConfig

) -> String { - let ServeConfig { - app, props, .. - } = cfg; + let ServeConfig { app, props, .. } = cfg; let mut vdom = VirtualDom::new_with_props(*app, props.clone()); @@ -55,6 +53,41 @@ impl SSRState { // serialize the props let _ = crate::props_html::serialize_props::encode_in_element(&cfg.props, &mut html); + #[cfg(all(debug_assertions, feature = "hot-reload"))] + { + let disconnect_js = r#"(function () { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const url = protocol + '//' + window.location.host + '/_dioxus/disconnect'; + const poll_interval = 1000; + const reload_upon_connect = () => { + console.log('Disconnected from server. Attempting to reconnect...'); + window.setTimeout( + () => { + // Try to reconnect to the websocket + const ws = new WebSocket(url); + ws.onopen = () => { + // If we reconnect, reload the page + window.location.reload(); + } + // Otherwise, try again in a second + reload_upon_connect(); + }, + poll_interval); + }; + + // on initial page load connect to the disconnect ws + const ws = new WebSocket(url); + ws.onopen = () => { + // if we disconnect, start polling + ws.onclose = reload_upon_connect; + }; +})()"#; + + html += r#""#; + } + html += &index.post_main; html From 126ed2f9b8cc44fecf9516cd4525f6a744ecfc57 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 1 May 2023 11:28:20 -0500 Subject: [PATCH 395/516] remove log --- packages/server/src/adapters/axum_adapter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 541094674..f4b05e199 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -326,7 +326,6 @@ async fn render_handler( State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, request: Request, ) -> impl IntoResponse { - println!("Rendering"); let (parts, _) = request.into_parts(); let parts: Arc = Arc::new(parts.into()); let server_context = DioxusServerContext::new(parts); From a78ee4ffe77ad68a2b6e677446853bef3b086584 Mon Sep 17 00:00:00 2001 From: stephenandary Date: Mon, 1 May 2023 14:12:08 -0400 Subject: [PATCH 396/516] WIP - Update devcontainer to use debian bookworm --- .devcontainer/Dockerfile | 9 +++++++++ .devcontainer/devcontainer.json | 15 ++++++++++++--- .../linux/devcontainer-feature.json | 4 ---- .devcontainer/local-features/linux/install.sh | 6 ------ 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/local-features/linux/devcontainer-feature.json delete mode 100644 .devcontainer/local-features/linux/install.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..b20e962b9 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,9 @@ +# [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye +ARG VARIANT="slim-bookworm" +FROM rust:${VARIANT} +ENV DEBIAN_FRONTEND noninteractive +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive + +RUN apt-get -qq install build-essential libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1c5278332..aa536ab53 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,9 +1,18 @@ { "name": "dioxus", - "image": "mcr.microsoft.com/devcontainers/rust:latest", + "remoteUser": "vscode", + "build": { + "dockerfile": "./Dockerfile", + "context": "." + }, "features": { - "ghcr.io/devcontainers/features/common-utils:2": {}, - "./local-features/linux": {} + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": "true", + "username": "vscode", + "uid": "1000", + "gid": "1000", + "upgradePackages": "true" + } }, "containerEnv": { "RUST_LOG": "INFO" diff --git a/.devcontainer/local-features/linux/devcontainer-feature.json b/.devcontainer/local-features/linux/devcontainer-feature.json deleted file mode 100644 index daba5bc1f..000000000 --- a/.devcontainer/local-features/linux/devcontainer-feature.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "id": "linux", - "version": "0.0.1" -} \ No newline at end of file diff --git a/.devcontainer/local-features/linux/install.sh b/.devcontainer/local-features/linux/install.sh deleted file mode 100644 index 8f9ad5e1a..000000000 --- a/.devcontainer/local-features/linux/install.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -echo "Update Apt Database" -sudo apt-get update - -echo "Install Webkit Dependencies" -sudo apt-get -qq install build-essential libwebkit2gtk-4.0-dev libgtk-3-dev libayatana-appindicator3-dev From 856650eb4d4c898af323fa4750e0a1bf2db9ac6a Mon Sep 17 00:00:00 2001 From: stephenandary Date: Mon, 1 May 2023 14:25:54 -0400 Subject: [PATCH 397/516] WIP - Add README --- .devcontainer/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .devcontainer/README.md diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000..74dbbe8c5 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,26 @@ +# Dev Container + +A dev container in the most simple context allows one to create a consistent development environment within a docker container that can easily be opened locally or remotely via codespaces such that contributors don't need to install anything to contribute. + +## Useful Links + +- +- +- +- + +## Using A Dev Container + +### Locally + +To use this dev container locally, make sure Docker is installed and in VSCode install the `ms-vscode-remote.remote-containers` extension. Then from the root of Dioxus you can type `Ctrl + Shift + P`, then choose `Dev Containers: Rebuild and Reopen in Devcontainer`. + +### Codespaces + +TBD + +## Troubleshooting + +If having difficulty commiting with github, and you use ssh or gpg keys, you may need to ensure that the keys are being shared properly between your host and VSCode. + +Though VSCode does a pretty good job sharing credentials between host and devcontainer, to save some time you can always just reopen the container locally to commit with `Ctrl + Shift + P`, then choose `Dev Containers: Reopen Folder Locally` From e68c806f8ce294cc855c116db3a2ba0d26cc6bd3 Mon Sep 17 00:00:00 2001 From: stephenandary Date: Mon, 1 May 2023 14:27:47 -0400 Subject: [PATCH 398/516] WIP - Update Readme Codespaces section with setup link --- .devcontainer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 74dbbe8c5..570435663 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -17,7 +17,7 @@ To use this dev container locally, make sure Docker is installed and in VSCode i ### Codespaces -TBD +[Codespaces Setup](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository) ## Troubleshooting From 5f3c3d3f04217fcd1d58b5a182ee19dfba369037 Mon Sep 17 00:00:00 2001 From: stephenandary Date: Mon, 1 May 2023 14:49:38 -0400 Subject: [PATCH 399/516] WIP - Update base image to nightly so format on save works --- .devcontainer/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b20e962b9..c0e58fab8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ # [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye -ARG VARIANT="slim-bookworm" -FROM rust:${VARIANT} +ARG VARIANT="nightly-bookworm-slim" +FROM rustlang/rust:${VARIANT} ENV DEBIAN_FRONTEND noninteractive RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections From d3cf9f728c6d501944c143baa1e39d77ac9b0f45 Mon Sep 17 00:00:00 2001 From: stephenandary Date: Mon, 1 May 2023 14:54:21 -0400 Subject: [PATCH 400/516] WIP - remove comment in Dockerfile --- .devcontainer/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c0e58fab8..e2fd03ad8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,3 @@ -# [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye ARG VARIANT="nightly-bookworm-slim" FROM rustlang/rust:${VARIANT} ENV DEBIAN_FRONTEND noninteractive From 064bee4a9ff3d481622e56ed1e6da5a391020fa7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 1 May 2023 17:34:30 -0500 Subject: [PATCH 401/516] spawn hot reloading in a seperate thread --- .../examples/axum-hello-world/Cargo.toml | 8 +----- .../examples/axum-hello-world/src/main.rs | 15 ++++++++++- .../server/examples/axum-router/Cargo.toml | 8 +----- .../server/examples/axum-router/src/main.rs | 13 +++++++++ .../examples/salvo-hello-world/Cargo.toml | 8 +----- .../examples/salvo-hello-world/src/main.rs | 13 +++++++++ .../examples/warp-hello-world/Cargo.toml | 8 +----- .../examples/warp-hello-world/src/main.rs | 15 ++++++++++- packages/server/src/adapters/axum_adapter.rs | 18 +++++-------- packages/server/src/adapters/salvo_adapter.rs | 15 +++++------ packages/server/src/adapters/warp_adapter.rs | 27 ++++++++++--------- packages/server/src/hot_reload.rs | 15 +++++++++++ packages/server/src/render.rs | 8 +++--- 13 files changed, 106 insertions(+), 65 deletions(-) diff --git a/packages/server/examples/axum-hello-world/Cargo.toml b/packages/server/examples/axum-hello-world/Cargo.toml index b2c14561d..27c6af08b 100644 --- a/packages/server/examples/axum-hello-world/Cargo.toml +++ b/packages/server/examples/axum-hello-world/Cargo.toml @@ -13,15 +13,9 @@ dioxus-server = { path = "../../" } axum = { version = "0.6.12", optional = true } tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" +execute = "0.2.12" [features] default = ["web"] ssr = ["axum", "tokio", "dioxus-server/axum"] web = ["dioxus-web"] - -[profile.release] -lto = true -panic = "abort" -opt-level = 3 -strip = true -codegen-units = 1 diff --git a/packages/server/examples/axum-hello-world/src/main.rs b/packages/server/examples/axum-hello-world/src/main.rs index 6b6f32db7..3db117c02 100644 --- a/packages/server/examples/axum-hello-world/src/main.rs +++ b/packages/server/examples/axum-hello-world/src/main.rs @@ -19,6 +19,19 @@ fn main() { ); #[cfg(feature = "ssr")] { + // Start hot reloading + hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| { + execute::shell("dioxus build --features web") + .spawn() + .unwrap() + .wait() + .unwrap(); + execute::shell("cargo run --features ssr --no-default-features") + .spawn() + .unwrap(); + true + })); + PostServerData::register().unwrap(); GetServerData::register().unwrap(); tokio::runtime::Runtime::new() @@ -65,7 +78,7 @@ fn app(cx: Scope) -> Element { } } }, - "Run a server function" + "Run a server function! testing1234" } "Server said: {text}" }) diff --git a/packages/server/examples/axum-router/Cargo.toml b/packages/server/examples/axum-router/Cargo.toml index 3e137d8f6..2ba621c9c 100644 --- a/packages/server/examples/axum-router/Cargo.toml +++ b/packages/server/examples/axum-router/Cargo.toml @@ -16,15 +16,9 @@ tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" tower-http = { version = "0.4.0", features = ["fs"], optional = true } http = { version = "0.2.9", optional = true } +execute = "0.2.12" [features] default = ["web"] ssr = ["axum", "tokio", "dioxus-server/axum", "tower-http", "http"] web = ["dioxus-web", "dioxus-router/web"] - -[profile.release] -lto = true -panic = "abort" -opt-level = 3 -strip = true -codegen-units = 1 diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs index 3994eeb51..0b5e55e4f 100644 --- a/packages/server/examples/axum-router/src/main.rs +++ b/packages/server/examples/axum-router/src/main.rs @@ -20,6 +20,19 @@ fn main() { ); #[cfg(feature = "ssr")] { + // Start hot reloading + hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| { + execute::shell("dioxus build --features web") + .spawn() + .unwrap() + .wait() + .unwrap(); + execute::shell("cargo run --features ssr --no-default-features") + .spawn() + .unwrap(); + true + })); + use axum::extract::State; PostServerData::register().unwrap(); GetServerData::register().unwrap(); diff --git a/packages/server/examples/salvo-hello-world/Cargo.toml b/packages/server/examples/salvo-hello-world/Cargo.toml index 9146ce3e2..50c92f237 100644 --- a/packages/server/examples/salvo-hello-world/Cargo.toml +++ b/packages/server/examples/salvo-hello-world/Cargo.toml @@ -13,15 +13,9 @@ dioxus-server = { path = "../../" } tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" salvo = { version = "0.37.9", optional = true } +execute = "0.2.12" [features] default = ["web"] ssr = ["salvo", "tokio", "dioxus-server/salvo"] web = ["dioxus-web"] - -[profile.release] -lto = true -panic = "abort" -opt-level = 3 -strip = true -codegen-units = 1 diff --git a/packages/server/examples/salvo-hello-world/src/main.rs b/packages/server/examples/salvo-hello-world/src/main.rs index ff1081077..25ebbbff9 100644 --- a/packages/server/examples/salvo-hello-world/src/main.rs +++ b/packages/server/examples/salvo-hello-world/src/main.rs @@ -19,6 +19,19 @@ fn main() { ); #[cfg(feature = "ssr")] { + // Start hot reloading + hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| { + execute::shell("dioxus build --features web") + .spawn() + .unwrap() + .wait() + .unwrap(); + execute::shell("cargo run --features ssr --no-default-features") + .spawn() + .unwrap(); + true + })); + use salvo::prelude::*; PostServerData::register().unwrap(); GetServerData::register().unwrap(); diff --git a/packages/server/examples/warp-hello-world/Cargo.toml b/packages/server/examples/warp-hello-world/Cargo.toml index e182494b0..2c5d175ae 100644 --- a/packages/server/examples/warp-hello-world/Cargo.toml +++ b/packages/server/examples/warp-hello-world/Cargo.toml @@ -13,15 +13,9 @@ dioxus-server = { path = "../../" } tokio = { version = "1.27.0", features = ["full"], optional = true } serde = "1.0.159" warp = { version = "0.3.3", optional = true } +execute = "0.2.12" [features] default = ["web"] ssr = ["warp", "tokio", "dioxus-server/warp"] web = ["dioxus-web"] - -[profile.release] -lto = true -panic = "abort" -opt-level = 3 -strip = true -codegen-units = 1 diff --git a/packages/server/examples/warp-hello-world/src/main.rs b/packages/server/examples/warp-hello-world/src/main.rs index 04684f803..3bbb95227 100644 --- a/packages/server/examples/warp-hello-world/src/main.rs +++ b/packages/server/examples/warp-hello-world/src/main.rs @@ -19,6 +19,19 @@ fn main() { ); #[cfg(feature = "ssr")] { + // Start hot reloading + hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| { + execute::shell("dioxus build --features web") + .spawn() + .unwrap() + .wait() + .unwrap(); + execute::shell("cargo run --features ssr --no-default-features") + .spawn() + .unwrap(); + true + })); + PostServerData::register().unwrap(); GetServerData::register().unwrap(); tokio::runtime::Runtime::new() @@ -45,7 +58,7 @@ fn app(cx: Scope) -> Element { cx.render(rsx! { h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count += 10, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| { diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index f4b05e199..6ca82b1e5 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -290,11 +290,7 @@ where self.nest( "/_dioxus", Router::new() - .route( - "/hot_reload", - get(hot_reload_handler) - .with_state(crate::hot_reload::HotReloadState::default()), - ) + .route("/hot_reload", get(hot_reload_handler)) .route( "/disconnect", get(|ws: WebSocketUpgrade| async { @@ -420,14 +416,13 @@ fn report_err(e: E) -> Response { /// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change. #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] -pub async fn hot_reload_handler( - ws: WebSocketUpgrade, - State(state): State, -) -> impl IntoResponse { +pub async fn hot_reload_handler(ws: WebSocketUpgrade) -> impl IntoResponse { use axum::extract::ws::Message; use futures_util::StreamExt; - ws.on_upgrade(|mut socket| async move { + let state = crate::hot_reload::spawn_hot_reload().await; + + ws.on_upgrade(move |mut socket| async move { println!("🔥 Hot Reload WebSocket connected"); { // update any rsx calls that changed before the websocket connected. @@ -448,7 +443,8 @@ pub async fn hot_reload_handler( println!("finished"); } - let mut rx = tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver); + let mut rx = + tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver.clone()); while let Some(change) = rx.next().await { if let Some(template) = change { let template = { serde_json::to_string(&template).unwrap() }; diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 3751211e4..75387d1f9 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -455,9 +455,7 @@ impl HotReloadHandler { /// A handler for Dioxus web hot reload websocket. This will send the updated static parts of the RSX to the client when they change. #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] #[derive(Default)] -pub struct HotReloadHandler { - state: crate::hot_reload::HotReloadState, -} +pub struct HotReloadHandler; #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] #[handler] @@ -471,10 +469,10 @@ impl HotReloadHandler { use salvo::ws::Message; use salvo::ws::WebSocketUpgrade; - let state = self.state.clone(); + let state = crate::hot_reload::spawn_hot_reload().await; WebSocketUpgrade::new() - .upgrade(req, res, |mut websocket| async move { + .upgrade(req, res, move |mut websocket| async move { use futures_util::StreamExt; println!("🔥 Hot Reload WebSocket connected"); @@ -497,8 +495,9 @@ impl HotReloadHandler { println!("finished"); } - let mut rx = - tokio_stream::wrappers::WatchStream::from_changes(state.message_receiver); + let mut rx = tokio_stream::wrappers::WatchStream::from_changes( + state.message_receiver.clone(), + ); while let Some(change) = rx.next().await { if let Some(template) = change { let template = { serde_json::to_string(&template).unwrap() }; @@ -518,7 +517,7 @@ async fn ignore_ws(req: &mut Request, res: &mut Response) -> Result<(), salvo::h use salvo::ws::WebSocketUpgrade; WebSocketUpgrade::new() .upgrade(req, res, |mut ws| async move { - let _ = dbg!(ws.send(salvo::ws::Message::text("connected")).await); + let _ = ws.send(salvo::ws::Message::text("connected")).await; while let Some(msg) = ws.recv().await { if msg.is_err() { return; diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 3d41d76cf..9a4ec7af4 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -372,12 +372,10 @@ pub fn connect_hot_reload() -> impl Filter impl Filter impl Filter); @@ -432,10 +433,12 @@ pub fn connect_hot_reload() -> impl Filter = tokio::sync::OnceCell::const_new(); +pub(crate) async fn spawn_hot_reload() -> &'static HotReloadState { + HOT_RELOAD_STATE + .get_or_init(|| async { + println!("spinning up hot reloading"); + let r = tokio::task::spawn_blocking(HotReloadState::default) + .await + .unwrap(); + println!("hot reloading ready"); + r + }) + .await +} diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index 1d32e2acd..a05a63801 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -55,6 +55,7 @@ impl SSRState { #[cfg(all(debug_assertions, feature = "hot-reload"))] { + // In debug mode, we need to add a script to the page that will reload the page if the websocket disconnects to make full recompile hot reloads work let disconnect_js = r#"(function () { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const url = protocol + '//' + window.location.host + '/_dioxus/disconnect'; @@ -77,10 +78,9 @@ impl SSRState { // on initial page load connect to the disconnect ws const ws = new WebSocket(url); - ws.onopen = () => { - // if we disconnect, start polling - ws.onclose = reload_upon_connect; - }; + // if we disconnect, start polling + ws.onmessage = (m) => {console.log(m)}; + ws.onclose = reload_upon_connect; })()"#; html += r#" From f63220019ddaac5a8bc44db855723c245d53632e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 8 Jun 2023 15:08:40 +0000 Subject: [PATCH 480/516] bump liveview version --- packages/liveview/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 302e4c486..498d92c89 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-liveview" -version = "0.3.0" +version = "0.3.1" edition = "2021" repository = "https://github.com/DioxusLabs/dioxus/" homepage = "https://dioxuslabs.com" From 1546fce485caa329a3885d38405ed7ad3f7c6cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Sat, 10 Jun 2023 18:26:02 +0200 Subject: [PATCH 481/516] fix: Properly drop UseFuture's values to avoid leaking memory --- packages/hooks/src/usefuture.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/hooks/src/usefuture.rs b/packages/hooks/src/usefuture.rs index f540e0a6e..18bf50310 100644 --- a/packages/hooks/src/usefuture.rs +++ b/packages/hooks/src/usefuture.rs @@ -89,6 +89,14 @@ pub struct UseFuture { values: Rc>>, } +impl Drop for UseFuture { + fn drop(&mut self) { + for value in self.values.take().into_iter() { + drop(unsafe { Box::from_raw(value) }) + } + } +} + pub enum UseFutureState<'a, T> { Pending, Complete(&'a T), From 741098e893a329514e3970bc80073e2209d3d2a7 Mon Sep 17 00:00:00 2001 From: David James <4576329+xpe@users.noreply.github.com> Date: Sat, 10 Jun 2023 22:45:44 -0400 Subject: [PATCH 482/516] Fix typo: "NetWork" -> "Network" "W" -> "w". In ASCII, that is +32 :) --- src/server/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index 59bab0b64..8e5861270 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -624,7 +624,7 @@ fn print_console_info(ip: &String, port: u16, config: &CrateConfig, options: Pre format!("http://localhost:{}/", port).blue() ); println!( - "\t> NetWork : {}", + "\t> Network : {}", format!("http://{}:{}/", ip, port).blue() ); println!(""); From ae80702ed9203768023271d6085c4db5ed6cf489 Mon Sep 17 00:00:00 2001 From: erithax Date: Fri, 16 Jun 2023 17:58:38 +0200 Subject: [PATCH 483/516] fix chosing launch target it used to match in order: * first binary alphabetically * library now matches in order: 1. default-run binary 2. binary with name of crate or located at src/main.rs 3. first binary alphabetically 4. library --- src/config.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/config.rs b/src/config.rs index e4dd7c416..9c87e266e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -186,15 +186,21 @@ impl CrateConfig { let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap(); - // We just assume they're using a 'main.rs' - // Anyway, we've already parsed the manifest, so it should be easy to change the type - let output_filename = manifest - .bin - .first() - .or(manifest.lib.as_ref()) - .and_then(|product| product.name.clone()) - .or_else(|| manifest.package.as_ref().map(|pkg| pkg.name.clone())) - .expect("No lib found from cargo metadata"); + let output_filename = { + match &manifest.package.as_ref().unwrap().default_run { + Some(default_run_target) => { + default_run_target.to_owned() + }, + None => { + manifest.bin.iter().find(|b| b.name == manifest.package.as_ref().map(|pkg| pkg.name.clone())) + .or(manifest.bin.iter().find(|b| b.path == Some("src/main.rs".to_owned()))) + .or(manifest.bin.first()) + .or(manifest.lib.as_ref()) + .and_then(|prod| prod.name.clone()) + .expect("No executable or library found from cargo metadata.") + } + } + }; let executable = ExecutableType::Binary(output_filename); let release = false; From b48cfe5730c73ac2d61106a33a57efb87286a839 Mon Sep 17 00:00:00 2001 From: Nick Mosher Date: Mon, 19 Jun 2023 09:29:29 -0400 Subject: [PATCH 484/516] fix: Remove dbg that was causing TUI rendering problems (#1085) --- packages/core/src/virtual_dom.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index fe4d3e35b..b118cd59a 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -426,7 +426,7 @@ impl VirtualDom { } } - parent_path = dbg!(template.parent).and_then(|id| self.elements.get(id.0)); + parent_path = template.parent.and_then(|id| self.elements.get(id.0)); } else { break; } From 96802c27ef14625a349b9491fd10150a97e9897b Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 19 Jun 2023 06:46:52 -0700 Subject: [PATCH 485/516] fixed typo in README (#1098) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40981401e..9a1c347c9 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ fn app(cx: Scope) -> Element { } ``` -Dioxus can be used to deliver webapps, desktop apps, static sites, mobile apps, TUI apps, liveview apps, and more. Dioxus is entirely renderer agnostic and can be used as platform for any renderer. +Dioxus can be used to deliver webapps, desktop apps, static sites, mobile apps, TUI apps, liveview apps, and more. Dioxus is entirely renderer agnostic and can be used as a platform for any renderer. If you know React, then you already know Dioxus. From d596967b4008739ee328ec213fedc24b53987942 Mon Sep 17 00:00:00 2001 From: Jon Earnshaw <1021321+jpearnshaw@users.noreply.github.com> Date: Mon, 19 Jun 2023 14:49:43 +0100 Subject: [PATCH 486/516] Fix typos on Event Handlers page (#1102) * Fix typo on Event Handlers page * Fix 2nd typo on Event Handlers page --- docs/guide/src/en/interactivity/event_handlers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index 317038923..e1057f98b 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -29,7 +29,7 @@ To learn what the different event types for HTML provide, read the [events modul Some events will trigger first on the element the event originated at upward. For example, a click event on a `button` inside a `div` would first trigger the button's event listener and then the div's event listener. -> For more information about event propigation see [the mdn docs on event bubling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling) +> For more information about event propagation see [the mdn docs on event bubbling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling) If you want to prevent this behavior, you can call `stop_propagation()` on the event: From 6e5fbcff6384ee0f7ef50abc22c4c3cca7aa6d70 Mon Sep 17 00:00:00 2001 From: James Fletcher <38212943+jmsfltchr@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:26:02 +0100 Subject: [PATCH 487/516] Increase the number of tokio channels for the query engine from 8 to 1000. (#1095) --- packages/desktop/src/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/src/query.rs b/packages/desktop/src/query.rs index bd8b2ee2a..fb23dc487 100644 --- a/packages/desktop/src/query.rs +++ b/packages/desktop/src/query.rs @@ -22,7 +22,7 @@ pub(crate) struct QueryEngine { impl Default for QueryEngine { fn default() -> Self { - let (sender, _) = tokio::sync::broadcast::channel(8); + let (sender, _) = tokio::sync::broadcast::channel(1000); Self { sender: Rc::new(sender), active_requests: SharedSlab::default(), From 4f672ff4bb4cc3dad61b36474ee42789c2353260 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 19 Jun 2023 10:19:35 -0500 Subject: [PATCH 488/516] fix boolean attributes with raw boolean values --- packages/interpreter/src/common.js | 12 ++++++++---- packages/interpreter/src/sledgehammer_bindings.rs | 9 ++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/interpreter/src/common.js b/packages/interpreter/src/common.js index 532c231db..c93f9eb60 100644 --- a/packages/interpreter/src/common.js +++ b/packages/interpreter/src/common.js @@ -48,21 +48,25 @@ export function setAttributeInner(node, field, value, ns) { node.defaultValue = value; break; case "checked": - node.checked = value === "true" || value === true; + node.checked = truthy(value); break; case "selected": - node.selected = value === "true" || value === true; + node.selected = truthy(value); break; case "dangerous_inner_html": node.innerHTML = value; break; default: // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364 - if (value === "false" && bool_attrs.hasOwnProperty(name)) { + if (!truthy(value) && bool_attrs.hasOwnProperty(name)) { node.removeAttribute(name); } else { node.setAttribute(name, value); } } } -} \ No newline at end of file +} + +function truthy(val) { + return val === "true" || val === true; +} diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index 6c2804b59..e661a7c16 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -78,17 +78,17 @@ mod js { node.defaultValue = value; break; case "checked": - node.checked = value === "true"; + node.checked = truthy(value); break; case "selected": - node.selected = value === "true"; + node.selected = truthy(value); break; case "dangerous_inner_html": node.innerHTML = value; break; default: // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364 - if (value === "false" && bool_attrs.hasOwnProperty(name)) { + if (!truthy(value) && bool_attrs.hasOwnProperty(name)) { node.removeAttribute(name); } else { node.setAttribute(name, value); @@ -164,6 +164,9 @@ mod js { selected: true, truespeed: true, }; + function truthy(val) { + return val === "true" || val === true; + } "#; extern "C" { From 8a16eb4cb3552023a8e8b384b47abb7ccd64d13f Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Mon, 19 Jun 2023 19:43:33 +0200 Subject: [PATCH 489/516] replace unmaintained actions-rs/* actions in CI workflows (#1099) Basically all of the `actions-rs/*` actions are unmaintained. See for more information. Due to their age they generate several warnings in CI runs. To get rid of those warnings the occurrences of `actions-rs/toolchain` are replaced by `dtolnay/rust-toolchain`, and the occurrences of `actions-rs/cargo` are replaced by direct invocations of `cargo`. --- .github/workflows/macos.yml | 11 ++-------- .github/workflows/main.yml | 44 +++++++------------------------------ 2 files changed, 10 insertions(+), 45 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c5616322e..b80a7b1f5 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -30,14 +30,7 @@ jobs: name: Test Suite runs-on: macos-latest steps: - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - - uses: actions-rs/cargo@v1 - with: - command: test - args: --all --tests + - run: cargo test --all --tests diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3bff9fc6..1a4b156cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,30 +32,19 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: sudo apt-get update - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - uses: actions/checkout@v3 - - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --examples --tests + - run: cargo check --all --examples --tests test: if: github.event.pull_request.draft == false name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: sudo apt-get update - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev @@ -63,48 +52,31 @@ jobs: - uses: browser-actions/setup-firefox@latest - uses: jetli/wasm-pack-action@v0.4.0 - uses: actions/checkout@v3 - - uses: actions-rs/cargo@v1 - with: - command: make - args: tests + - run: cargo make tests fmt: if: github.event.pull_request.draft == false name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: rustup component add rustfmt - uses: actions/checkout@v3 - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + - run: cargo fmt --all -- --check clippy: if: github.event.pull_request.draft == false name: Clippy runs-on: ubuntu-latest steps: - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true + - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: sudo apt-get update - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - run: rustup component add clippy - uses: actions/checkout@v3 - - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --workspace --examples --tests -- -D warnings + - run: cargo clippy --workspace --examples --tests -- -D warnings # Coverage is disabled until we can fix it # coverage: From 5f873511c5019ba423a06c315b703acf48293f14 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 19 Jun 2023 14:29:11 -0500 Subject: [PATCH 490/516] use workspace dependancies --- Cargo.toml | 46 ++++++++++++++++--- packages/autofmt/Cargo.toml | 2 +- packages/core-macro/Cargo.toml | 2 +- packages/core/Cargo.toml | 14 +++--- packages/desktop/Cargo.toml | 24 +++++----- packages/dioxus-tui/Cargo.toml | 18 ++++---- packages/dioxus/Cargo.toml | 20 ++++---- packages/fermi/Cargo.toml | 4 +- packages/fullstack/Cargo.toml | 12 ++--- .../examples/axum-desktop/Cargo.toml | 10 ++-- .../examples/axum-hello-world/Cargo.toml | 8 ++-- .../fullstack/examples/axum-router/Cargo.toml | 10 ++-- .../examples/salvo-hello-world/Cargo.toml | 8 ++-- .../examples/warp-hello-world/Cargo.toml | 8 ++-- packages/hooks/Cargo.toml | 12 ++--- packages/hot-reload/Cargo.toml | 6 +-- packages/html/Cargo.toml | 8 ++-- packages/interpreter/Cargo.toml | 2 +- packages/liveview/Cargo.toml | 22 ++++----- packages/mobile/Cargo.toml | 2 +- packages/native-core-macro/Cargo.toml | 6 +-- packages/native-core/Cargo.toml | 14 +++--- packages/rink/Cargo.toml | 12 ++--- packages/router/Cargo.toml | 16 +++---- packages/rsx-rosetta/Cargo.toml | 4 +- packages/rsx/Cargo.toml | 2 +- packages/signals/Cargo.toml | 4 +- packages/ssr/Cargo.toml | 6 +-- packages/web/Cargo.toml | 22 ++++----- 29 files changed, 179 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 03b5c1296..9320b7783 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,40 @@ members = [ "examples/PWA-example", ] +# dependencies that are shared across packages +[workspace.dependencies] +dioxus = { path = "packages/dioxus" } +dioxus-core = { path = "packages/core" } +dioxus-core-macro = { path = "packages/core-macro" } +dioxus-router = { path = "packages/router" } +dioxus-html = { path = "packages/html" } +dioxus-hooks = { path = "packages/hooks" } +dioxus-web = { path = "packages/web" } +dioxus-ssr = { path = "packages/ssr" } +dioxus-desktop = { path = "packages/desktop" } +dioxus-mobile = { path = "packages/mobile" } +dioxus-interpreter-js = { path = "packages/interpreter" } +fermi = { path = "packages/fermi" } +dioxus-liveview = { path = "packages/liveview" } +dioxus-autofmt = { path = "packages/autofmt" } +dioxus-rsx = { path = "packages/rsx" } +dioxus-tui = { path = "packages/dioxus-tui" } +rink = { path = "packages/rink" } +dioxus-native-core = { path = "packages/native-core" } +dioxus-native-core-macro = { path = "packages/native-core-macro" } +dioxus-rsx-rosetta = { path = "packages/rsx-rosetta" } +dioxus-signals = { path = "packages/signals" } +dioxus-hot-reload = { path = "packages/hot-reload" } +dioxus-fullstack = { path = "packages/fullstack" } +dioxus_server_macro = { path = "packages/fullstack/server-macro" } +log = "0.4.19" +tokio = "1.28" +slab = "0.4.2" +futures-channel = "0.3.21" +futures-util = { version = "0.3", default-features = false } +rustc-hash = "1.1.0" +wasm-bindgen = "0.2.79" + # This is a "virtual package" # It is not meant to be published, but is used so "cargo run --example XYZ" works properly [package] @@ -52,12 +86,12 @@ rust-version = "1.60.0" publish = false [dev-dependencies] -dioxus = { path = "./packages/dioxus" } -dioxus-desktop = { path = "./packages/desktop", features = ["transparent"] } -dioxus-ssr = { path = "./packages/ssr" } -dioxus-router = { path = "./packages/router" } -dioxus-signals = { path = "./packages/signals" } -fermi = { path = "./packages/fermi" } +dioxus = { workspace = true } +dioxus-desktop = { workspace = true, features = ["transparent"] } +dioxus-ssr = { workspace = true } +dioxus-router = { workspace = true } +dioxus-signals = { workspace = true } +fermi = { workspace = true } futures-util = "0.3.21" log = "0.4.14" num-format = "0.4.0" diff --git a/packages/autofmt/Cargo.toml b/packages/autofmt/Cargo.toml index ccb6fed24..1c947dc21 100644 --- a/packages/autofmt/Cargo.toml +++ b/packages/autofmt/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-rsx = { path = "../rsx", version = "^0.0.3" } +dioxus-rsx = { workspace = true } proc-macro2 = { version = "1.0.6", features = ["span-locations"] } quote = "1.0" syn = { version = "1.0.11", features = ["full", "extra-traits"] } diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index 843470646..2f9eef4d4 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true proc-macro2 = { version = "1.0" } quote = "1.0" syn = { version = "1.0", features = ["full", "extra-traits"] } -dioxus-rsx = { path = "../rsx", version = "^0.0.3" } +dioxus-rsx = { workspace = true } # testing [dev-dependencies] diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index af0feba9c..3fd0c62cf 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -18,23 +18,23 @@ keywords = ["dom", "ui", "gui", "react"] bumpalo = { version = "3.6", features = ["collections", "boxed"] } # faster hashmaps -rustc-hash = "1.1.0" +rustc-hash = { workspace = true } # Used in diffing longest-increasing-subsequence = "0.1.0" -futures-util = { version = "0.3", default-features = false, features = [ +futures-util = { workspace = true, default-features = false, features = [ "alloc", ] } -slab = "0.4" +slab = { workspace = true } -futures-channel = "0.3.21" +futures-channel = { workspace = true } indexmap = "1.7" smallbox = "0.8.1" -log = "0.4.17" +log = { workspace = true } # Serialize the Edits for use in Webview/Liveview instances serde = { version = "1", features = ["derive"], optional = true } @@ -42,8 +42,8 @@ serde = { version = "1", features = ["derive"], optional = true } bumpslab = { version = "0.2.0" } [dev-dependencies] -tokio = { version = "1", features = ["full"] } -dioxus = { path = "../dioxus" } +tokio = { workspace = true, features = ["full"] } +dioxus = { workspace = true } pretty_assertions = "1.3.0" rand = "0.8.5" diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 35e63bd9d..dbb8e6c0f 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -12,18 +12,18 @@ keywords = ["dom", "ui", "gui", "react"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] } -dioxus-html = { path = "../html", features = ["serialize", "native-bind"], version = "^0.3.0" } -dioxus-interpreter-js = { path = "../interpreter", version = "^0.3.0" } -dioxus-hot-reload = { path = "../hot-reload", optional = true } +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-html = { workspace = true, features = ["serialize", "native-bind"] } +dioxus-interpreter-js = { workspace = true } +dioxus-hot-reload = { workspace = true, optional = true } serde = "1.0.136" serde_json = "1.0.79" thiserror = "1.0.30" -log = "0.4.14" +log = { workspace = true } wry = { version = "0.27.2" } -futures-channel = "0.3.21" -tokio = { version = "1.27", features = [ +futures-channel = { workspace = true } +tokio = { workspace = true, features = [ "sync", "rt-multi-thread", "rt", @@ -34,9 +34,9 @@ tokio = { version = "1.27", features = [ webbrowser = "0.8.0" infer = "0.11.0" dunce = "1.0.2" -slab = "0.4" +slab = { workspace = true } -futures-util = "0.3.25" +futures-util = { workspace = true } rfd = "0.11.3" urlencoding = "2.1.2" @@ -56,9 +56,9 @@ tray = ["wry/tray"] hot-reload = ["dioxus-hot-reload"] [dev-dependencies] -dioxus-core-macro = { path = "../core-macro" } -dioxus-hooks = { path = "../hooks" } -dioxus = { path = "../dioxus" } +dioxus-core-macro = { workspace = true } +dioxus-hooks = { workspace = true } +dioxus = { workspace = true } exitcode = "1.1.2" scraper = "0.16.0" diff --git a/packages/dioxus-tui/Cargo.toml b/packages/dioxus-tui/Cargo.toml index 7058036d1..528031a2c 100644 --- a/packages/dioxus-tui/Cargo.toml +++ b/packages/dioxus-tui/Cargo.toml @@ -13,21 +13,21 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus = { path = "../dioxus", version = "^0.3.0" } -dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] } -dioxus-html = { path = "../html", version = "^0.3.0" } -dioxus-native-core = { path = "../native-core", version = "^0.2.0", features = ["dioxus"] } -dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.3.0" } -dioxus-hot-reload = { path = "../hot-reload", optional = true } -rink = { path = "../rink" } +dioxus = { workspace = true } +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-html = { workspace = true } +dioxus-native-core = { workspace = true, features = ["dioxus"] } +dioxus-native-core-macro = { workspace = true } +dioxus-hot-reload = { workspace = true, optional = true } +rink = { workspace = true } crossterm = "0.23.0" -tokio = { version = "1.15.0", features = ["full"] } +tokio = { workspace = true, features = ["full"] } futures = "0.3.19" taffy = "0.3.12" [dev-dependencies] -dioxus = { path = "../dioxus" } +dioxus = { workspace = true } tokio = { version = "1" } criterion = "0.3.5" diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 8f0cc2184..456a10085 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -12,14 +12,14 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] rust-version = "1.65.0" [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0" } -dioxus-html = { path = "../html", version = "^0.3.0", optional = true } -dioxus-core-macro = { path = "../core-macro", version = "^0.3.0", optional = true } -dioxus-hooks = { path = "../hooks", version = "^0.3.0", optional = true } -dioxus-rsx = { path = "../rsx", version = "^0.0.3", optional = true } +dioxus-core = { workspace = true } +dioxus-html = { workspace = true, optional = true } +dioxus-core-macro = { workspace = true, optional = true } +dioxus-hooks = { workspace = true, optional = true } +dioxus-rsx = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus-hot-reload = { path = "../hot-reload", version = "0.1.0", optional = true } +dioxus-hot-reload = { workspace = true, optional = true } [features] default = ["macro", "hooks", "html", "hot-reload"] @@ -30,14 +30,14 @@ hot-reload = ["dioxus-hot-reload"] [dev-dependencies] -futures-util = "0.3.21" -log = "0.4.14" +futures-util = { workspace = true } +log = { workspace = true } rand = { version = "0.8.4", features = ["small_rng"] } criterion = "0.3.5" thiserror = "1.0.30" env_logger = "0.10.0" -tokio = { version = "1.21.2", features = ["full"] } -# dioxus-edit-stream = { path = "../edit-stream" } +tokio = { workspace = true, features = ["full"] } +# dioxus-edit-stream = { workspace = true } [[bench]] diff --git a/packages/fermi/Cargo.toml b/packages/fermi/Cargo.toml index 9591a4f25..40bbb1b40 100644 --- a/packages/fermi/Cargo.toml +++ b/packages/fermi/Cargo.toml @@ -13,9 +13,9 @@ keywords = ["dom", "ui", "gui", "react", "state-management"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0" } +dioxus-core = { workspace = true } im-rc = { version = "15.0.0", features = ["serde"] } -log = "0.4.14" +log = { workspace = true } [dev-dependencies] closure = "0.3.0" diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 5e7da25ca..dd7652017 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -30,27 +30,27 @@ salvo = { version = "0.37.7", optional = true, features = ["serve-static", "ws"] serde = "1.0.159" # Dioxus + SSR -dioxus-core = { path = "../core", version = "^0.3.0" } -dioxus-ssr = { path = "../ssr", version = "^0.3.0", optional = true } +dioxus-core = { workspace = true } +dioxus-ssr = { workspace = true, optional = true } hyper = { version = "0.14.25", optional = true } http = { version = "0.2.9", optional = true } -log = "0.4.17" +log = { workspace = true } once_cell = "1.17.1" thiserror = "1.0.40" -tokio = { version = "1.27.0", features = ["full"], optional = true } +tokio = { workspace = true, features = ["full"], optional = true } object-pool = "0.5.4" anymap = "0.12.1" serde_json = { version = "1.0.95", optional = true } tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } -futures-util = { version = "0.3.28", optional = true } +futures-util = { workspace = true, optional = true } postcard = { version = "1.0.4", features = ["use-std"] } yazi = "0.1.5" base64 = "0.21.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus-hot-reload = { path = "../hot-reload" } +dioxus-hot-reload = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } diff --git a/packages/fullstack/examples/axum-desktop/Cargo.toml b/packages/fullstack/examples/axum-desktop/Cargo.toml index c876e9d3d..459a6c9b7 100644 --- a/packages/fullstack/examples/axum-desktop/Cargo.toml +++ b/packages/fullstack/examples/axum-desktop/Cargo.toml @@ -7,12 +7,12 @@ publish = false [lib] [dependencies] -dioxus-desktop = { path = "../../../desktop", optional = true } -dioxus = { path = "../../../dioxus" } -dioxus-router = { path = "../../../router" } -dioxus-fullstack = { path = "../../" } +dioxus-desktop = { workspace = true, optional = true } +dioxus = { workspace = true } +dioxus-router = { workspace = true } +dioxus-fullstack = { workspace = true } axum = { version = "0.6.12", optional = true } -tokio = { version = "1.27.0", features = ["full"], optional = true } +tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" [features] diff --git a/packages/fullstack/examples/axum-hello-world/Cargo.toml b/packages/fullstack/examples/axum-hello-world/Cargo.toml index c1926b289..b41225206 100644 --- a/packages/fullstack/examples/axum-hello-world/Cargo.toml +++ b/packages/fullstack/examples/axum-hello-world/Cargo.toml @@ -7,11 +7,11 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } -dioxus = { path = "../../../dioxus" } -dioxus-fullstack = { path = "../../" } +dioxus-web = { workspace = true, features=["hydrate"], optional = true } +dioxus = { workspace = true } +dioxus-fullstack = { workspace = true } axum = { version = "0.6.12", optional = true } -tokio = { version = "1.27.0", features = ["full"], optional = true } +tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" execute = "0.2.12" diff --git a/packages/fullstack/examples/axum-router/Cargo.toml b/packages/fullstack/examples/axum-router/Cargo.toml index db4138430..fbd59bfc0 100644 --- a/packages/fullstack/examples/axum-router/Cargo.toml +++ b/packages/fullstack/examples/axum-router/Cargo.toml @@ -7,12 +7,12 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } -dioxus = { path = "../../../dioxus" } -dioxus-router = { path = "../../../router" } -dioxus-fullstack = { path = "../../" } +dioxus-web = { workspace = true, features=["hydrate"], optional = true } +dioxus = { workspace = true } +dioxus-router = { workspace = true } +dioxus-fullstack = { workspace = true } axum = { version = "0.6.12", optional = true } -tokio = { version = "1.27.0", features = ["full"], optional = true } +tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" tower-http = { version = "0.4.0", features = ["fs"], optional = true } http = { version = "0.2.9", optional = true } diff --git a/packages/fullstack/examples/salvo-hello-world/Cargo.toml b/packages/fullstack/examples/salvo-hello-world/Cargo.toml index 27b7e73dc..6895b85f6 100644 --- a/packages/fullstack/examples/salvo-hello-world/Cargo.toml +++ b/packages/fullstack/examples/salvo-hello-world/Cargo.toml @@ -7,10 +7,10 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } -dioxus = { path = "../../../dioxus" } -dioxus-fullstack = { path = "../../" } -tokio = { version = "1.27.0", features = ["full"], optional = true } +dioxus-web = { workspace = true, features=["hydrate"], optional = true } +dioxus = { workspace = true } +dioxus-fullstack = { workspace = true } +tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" salvo = { version = "0.37.9", optional = true } execute = "0.2.12" diff --git a/packages/fullstack/examples/warp-hello-world/Cargo.toml b/packages/fullstack/examples/warp-hello-world/Cargo.toml index 18da0b249..58b842853 100644 --- a/packages/fullstack/examples/warp-hello-world/Cargo.toml +++ b/packages/fullstack/examples/warp-hello-world/Cargo.toml @@ -7,10 +7,10 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-web = { path = "../../../web", features=["hydrate"], optional = true } -dioxus = { path = "../../../dioxus" } -dioxus-fullstack = { path = "../../" } -tokio = { version = "1.27.0", features = ["full"], optional = true } +dioxus-web = { workspace = true, features=["hydrate"], optional = true } +dioxus = { workspace = true } +dioxus-fullstack = { workspace = true } +tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" warp = { version = "0.3.3", optional = true } execute = "0.2.12" diff --git a/packages/hooks/Cargo.toml b/packages/hooks/Cargo.toml index c7e82c46c..19d5e53bd 100644 --- a/packages/hooks/Cargo.toml +++ b/packages/hooks/Cargo.toml @@ -12,12 +12,12 @@ keywords = ["dom", "ui", "gui", "react"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../../packages/core", version = "^0.3.0" } -futures-channel = "0.3.21" -log = "0.4" +dioxus-core = { workspace = true } +futures-channel = { workspace = true } +log = { workspace = true } [dev-dependencies] -futures-util = { version = "0.3", default-features = false } -dioxus-core = { path = "../../packages/core", version = "^0.3.0" } -dioxus = { path = "../../packages/dioxus", version = "^0.3.0" } +futures-util = { workspace = true, default-features = false } +dioxus-core = { workspace = true } +dioxus = { workspace = true } diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 4809df480..50c4114a7 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -12,9 +12,9 @@ keywords = ["dom", "ui", "gui", "react", "hot-reloading"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-rsx = { path = "../rsx" , version = "^0.0.3" } -dioxus-core = { path = "../core", features = ["serialize"], version = "^0.3.2"} -dioxus-html = { path = "../html", features = ["hot-reload-context"], version = "^0.3.0" } +dioxus-rsx = { workspace = true } +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-html = { workspace = true, features = ["hot-reload-context"] } interprocess = { version = "1.2.1" } notify = "5.0.0" diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 3d00495ba..5a9a51aa8 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -11,17 +11,17 @@ documentation = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react"] [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0" } -dioxus-rsx = { path = "../rsx", version = "^0.0.3", optional = true } +dioxus-core = { workspace = true } +dioxus-rsx = { workspace = true, optional = true } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } -wasm-bindgen = { version = "0.2.79", optional = true } +wasm-bindgen = { workspace = true, optional = true } euclid = "0.22.7" enumset = "1.0.11" keyboard-types = "0.6.2" async-trait = "0.1.58" serde-value = "0.7.0" -tokio = { version = "1.27", features = ["fs", "io-util"], optional = true } +tokio = { workspace = true, features = ["fs", "io-util"], optional = true } rfd = { version = "0.11.3", optional = true } [dependencies.web-sys] diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index b34dad992..96fffdedc 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -wasm-bindgen = { version = "0.2.79", optional = true } +wasm-bindgen = { workspace = true, optional = true } js-sys = { version = "0.3.56", optional = true } web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] } sledgehammer_bindgen = { version = "0.2.1", optional = true } diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 498d92c89..515d0ec54 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -14,21 +14,21 @@ license = "MIT/Apache-2.0" [dependencies] thiserror = "1.0.38" -log = "0.4.14" -slab = "0.4" -futures-util = { version = "0.3.25", default-features = false, features = [ +log = { workspace = true } +slab = { workspace = true } +futures-util = { workspace = true, default-features = false, features = [ "sink", ] } -futures-channel = { version = "0.3.25", features = ["sink"] } -tokio = { version = "1.22.0", features = ["time", "macros"] } +futures-channel = { workspace = true } +tokio = { workspace = true, features = ["time", "macros"] } tokio-stream = { version = "0.1.11", features = ["net"] } tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" -dioxus-html = { path = "../html", features = ["serialize"], version = "^0.3.0" } -dioxus-core = { path = "../core", features = ["serialize"], version = "^0.3.0" } -dioxus-interpreter-js = { path = "../interpreter", version = "0.3.0" } -dioxus-hot-reload = { path = "../hot-reload", optional = true } +dioxus-html = { workspace = true, features = ["serialize"] } +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-interpreter-js = { workspace = true } +dioxus-hot-reload = { workspace = true, optional = true } # warp warp = { version = "0.3.3", optional = true } @@ -47,8 +47,8 @@ once_cell = "1.17.1" [dev-dependencies] pretty_env_logger = { version = "0.5.0" } -tokio = { version = "1.22.0", features = ["full"] } -dioxus = { path = "../dioxus", version = "0.3.0" } +tokio = { workspace = true, features = ["full"] } +dioxus = { workspace = true } warp = "0.3.3" axum = { version = "0.6.1", features = ["ws"] } salvo = { version = "0.37.7", features = ["affix", "ws"] } diff --git a/packages/mobile/Cargo.toml b/packages/mobile/Cargo.toml index 3595e76d7..6d900d704 100644 --- a/packages/mobile/Cargo.toml +++ b/packages/mobile/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-desktop = { path = "../desktop", version = "^0.3.0" } +dioxus-desktop = { workspace = true } [lib] doctest = false diff --git a/packages/native-core-macro/Cargo.toml b/packages/native-core-macro/Cargo.toml index ed8648f93..1f06f601e 100644 --- a/packages/native-core-macro/Cargo.toml +++ b/packages/native-core-macro/Cargo.toml @@ -20,7 +20,7 @@ quote = "1.0" [dev-dependencies] smallvec = "1.6" -rustc-hash = "1.1.0" +rustc-hash = { workspace = true } anymap = "0.12.1" -dioxus = { path = "../dioxus" } -dioxus-native-core = { path = "../native-core" } +dioxus = { workspace = true } +dioxus-native-core = { workspace = true } diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 7832a33ae..5e8d7d94b 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -11,14 +11,14 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0", optional = true } +dioxus-core = { workspace = true, optional = true } keyboard-types = "0.6.2" taffy = "0.3.12" smallvec = "1.6" -rustc-hash = "1.1.0" +rustc-hash = { workspace = true } anymap = "1.0.0-beta.2" -slab = "0.4" +slab = { workspace = true } parking_lot = { version = "0.12.1", features = ["send_guard"] } crossbeam-deque = "0.8.2" dashmap = "5.4.0" @@ -32,10 +32,10 @@ shipyard = { version = "0.6.2", features = ["proc", "std"], default-features = f [dev-dependencies] rand = "0.8.5" -dioxus = { path = "../dioxus", version = "^0.3.0" } -tokio = { version = "*", features = ["full"] } -dioxus-native-core = { path = ".", features = ["dioxus"] } -dioxus-native-core-macro = { path = "../native-core-macro" } +dioxus = { workspace = true } +tokio = { workspace = true, features = ["full"] } +dioxus-native-core = { workspace = true, features = ["dioxus"] } +dioxus-native-core-macro = { workspace = true } [features] default = [] diff --git a/packages/rink/Cargo.toml b/packages/rink/Cargo.toml index 3e19048dd..2b8fe90f7 100644 --- a/packages/rink/Cargo.toml +++ b/packages/rink/Cargo.toml @@ -13,20 +13,20 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-html = { path = "../html", version = "^0.3.0" } -dioxus-native-core = { path = "../native-core", version = "^0.2.0" } -dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.3.0" } +dioxus-html = { workspace = true } +dioxus-native-core = { workspace = true } +dioxus-native-core-macro = { workspace = true } tui = "0.17.0" crossterm = "0.23.0" anyhow = "1.0.42" -tokio = { version = "1.15.0", features = ["full"] } +tokio = { workspace = true, features = ["full"] } futures = "0.3.19" taffy = "0.3.12" smallvec = "1.6" -rustc-hash = "1.1.0" +rustc-hash = { workspace = true } anymap = "1.0.0-beta.2" -futures-channel = "0.3.25" +futures-channel = { workspace = true } shipyard = { version = "0.6.2", features = ["proc", "std"], default-features = false } once_cell = "1.17.1" diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index e1f442ab4..df6e1000a 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -11,8 +11,8 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus = { path = "../dioxus", version = "0.3.0"} -futures-channel = "0.3.21" +dioxus = { workspace = true} +futures-channel = { workspace = true } url = { version = "2.2.2", default-features = false } # for wasm @@ -28,12 +28,12 @@ web-sys = { version = "0.3", features = [ "Window", "Location", ], optional = true } -wasm-bindgen = { version = "0.2", optional = true } +wasm-bindgen = { workspace = true, optional = true } js-sys = { version = "0.3", optional = true } gloo-events = { version = "0.1.1", optional = true } -log = "0.4.14" +log = { workspace = true } thiserror = "1.0.30" -futures-util = "0.3.21" +futures-util = { workspace = true } serde = { version = "1", optional = true } serde_urlencoded = { version = "0.7.1", optional = true } @@ -48,13 +48,13 @@ console_error_panic_hook = "0.1.7" wasm-logger = "0.2.0" wasm-bindgen-test = "0.3" gloo-utils = "0.1.2" -dioxus-web = { path = "../web", version = "0.3.0" } -# dioxus-desktop = { path = "../desktop", optional = true } +dioxus-web = { workspace = true } +# dioxus-desktop = { workspace = true, optional = true } # not wasm [target.wasm32-unknown-unknown.dev-dependencies] -dioxus-router = { path = ".", features = ["web"] } +dioxus-router = { workspace = true, features = ["web"] } [dev-dependencies.web-sys] version = "0.3" diff --git a/packages/rsx-rosetta/Cargo.toml b/packages/rsx-rosetta/Cargo.toml index e6bc45225..5a096d56a 100644 --- a/packages/rsx-rosetta/Cargo.toml +++ b/packages/rsx-rosetta/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["dom", "ui", "gui", "react"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-autofmt = { path = "../autofmt", version = "0.3.0" } -dioxus-rsx = { path = "../rsx" , version = "^0.0.3" } +dioxus-autofmt = { workspace = true } +dioxus-rsx = { workspace = true } html_parser = "0.6.3" proc-macro2 = "1.0.49" quote = "1.0.23" diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index 055a97132..d4b997187 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react"] [dependencies] proc-macro2 = { version = "1.0", features = ["span-locations"] } -dioxus-core = { path = "../core", version = "^0.3.0"} +dioxus-core = { workspace = true} syn = { version = "1.0", features = ["full", "extra-traits"] } quote = { version = "1.0" } serde = { version = "1.0", features = ["derive"] } diff --git a/packages/signals/Cargo.toml b/packages/signals/Cargo.toml index 9a12c787d..3ccd6b9b8 100644 --- a/packages/signals/Cargo.toml +++ b/packages/signals/Cargo.toml @@ -6,5 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core" } -slab = "0.4.7" +dioxus-core = { workspace = true } +slab = { workspace = true } diff --git a/packages/ssr/Cargo.toml b/packages/ssr/Cargo.toml index 02900ca0e..ed1d443ea 100644 --- a/packages/ssr/Cargo.toml +++ b/packages/ssr/Cargo.toml @@ -13,13 +13,13 @@ keywords = ["dom", "ui", "gui", "react", "ssr"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] } +dioxus-core = { workspace = true, features = ["serialize"] } askama_escape = "0.10.3" [dev-dependencies] -dioxus = { path = "../dioxus", version = "0.3.0" } +dioxus = { workspace = true } thiserror = "1.0.23" -log = "0.4.13" +log = { workspace = true } fern = { version = "0.6.0", features = ["colored"] } anyhow = "1.0" argh = "0.1.4" diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 2a90ee9a6..26240d1e6 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -11,25 +11,25 @@ documentation = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] -dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] } -dioxus-html = { path = "../html", version = "^0.3.0", features = ["wasm-bind"] } -dioxus-interpreter-js = { path = "../interpreter", version = "^0.3.0", features = [ +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-html = { workspace = true, features = ["wasm-bind"] } +dioxus-interpreter-js = { workspace = true, features = [ "sledgehammer", "minimal_bindings", ] } js-sys = "0.3.56" -wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] } +wasm-bindgen = { workspace = true, features = ["enable-interning"] } wasm-bindgen-futures = "0.4.29" -log = "0.4.14" -rustc-hash = "1.1.0" +log = { workspace = true } +rustc-hash = { workspace = true } console_error_panic_hook = { version = "0.1.7", optional = true } once_cell = "1.9.0" anyhow = "1.0.53" gloo-timers = { version = "0.2.3", features = ["futures"] } -futures-util = "0.3.19" +futures-util = { workspace = true } smallstr = "0.2.0" -futures-channel = "0.3.21" +futures-channel = { workspace = true } serde_json = { version = "1.0" } serde = { version = "1.0" } serde-wasm-bindgen = "0.4.5" @@ -89,8 +89,8 @@ panic_hook = ["console_error_panic_hook"] hydrate = [] [dev-dependencies] -dioxus = { path = "../dioxus", version = "0.3.0" } +dioxus = { workspace = true } wasm-bindgen-test = "0.3.29" -dioxus-ssr = { path = "../ssr", version = "0.3.0"} +dioxus-ssr = { workspace = true} wasm-logger = "0.2.0" -dioxus-web = { path = ".", features = ["hydrate"] } +dioxus-web = { workspace = true, features = ["hydrate"] } From c39f55920c19ec8d4a6ba77b989511af1f542997 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 19 Jun 2023 14:57:58 -0500 Subject: [PATCH 491/516] fix CI tests --- packages/desktop/Cargo.toml | 2 +- packages/web/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index dbb8e6c0f..e2ee6ff3b 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -30,7 +30,7 @@ tokio = { workspace = true, features = [ "time", "macros", "fs", -], optional = true, default-features = false } +], optional = true } webbrowser = "0.8.0" infer = "0.11.0" dunce = "1.0.2" diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 26240d1e6..daa296bb8 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -27,7 +27,7 @@ console_error_panic_hook = { version = "0.1.7", optional = true } once_cell = "1.9.0" anyhow = "1.0.53" gloo-timers = { version = "0.2.3", features = ["futures"] } -futures-util = { workspace = true } +futures-util = { workspace = true, features = ["std", "async-await", "async-await-macro"] } smallstr = "0.2.0" futures-channel = { workspace = true } serde_json = { version = "1.0" } From 158852e5a0afe64deff5270116d4f83da53458b8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 19 Jun 2023 16:08:46 -0500 Subject: [PATCH 492/516] make regestering server functions optional --- docs/guide/examples/server_context.rs | 3 - docs/guide/examples/server_context_state.rs | 3 - docs/guide/examples/server_function.rs | 3 - packages/fullstack/Cargo.toml | 2 +- packages/fullstack/README.md | 1 - .../examples/axum-desktop/src/server.rs | 3 - .../examples/axum-hello-world/src/main.rs | 2 - .../examples/axum-router/src/main.rs | 2 - .../examples/salvo-hello-world/src/main.rs | 2 - .../examples/warp-hello-world/src/main.rs | 2 - packages/fullstack/server-macro/Cargo.toml | 4 +- packages/fullstack/server-macro/src/lib.rs | 2 +- .../fullstack/src/adapters/axum_adapter.rs | 15 ++- .../fullstack/src/adapters/salvo_adapter.rs | 24 ++--- .../fullstack/src/adapters/warp_adapter.rs | 17 ++-- packages/fullstack/src/lib.rs | 2 +- packages/fullstack/src/server_fn.rs | 98 ++++++++++++++----- 17 files changed, 106 insertions(+), 79 deletions(-) diff --git a/docs/guide/examples/server_context.rs b/docs/guide/examples/server_context.rs index 446b0a93a..eb3c5e9c1 100644 --- a/docs/guide/examples/server_context.rs +++ b/docs/guide/examples/server_context.rs @@ -16,9 +16,6 @@ fn main() { use axum::extract::State; use axum::routing::get; - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/docs/guide/examples/server_context_state.rs b/docs/guide/examples/server_context_state.rs index 390849ff6..c75496561 100644 --- a/docs/guide/examples/server_context_state.rs +++ b/docs/guide/examples/server_context_state.rs @@ -25,9 +25,6 @@ fn main() { use axum::routing::get; use std::sync::Arc; - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/docs/guide/examples/server_function.rs b/docs/guide/examples/server_function.rs index d803ea60d..55f5104f9 100644 --- a/docs/guide/examples/server_function.rs +++ b/docs/guide/examples/server_function.rs @@ -16,9 +16,6 @@ fn main() { use axum::extract::State; use axum::routing::get; - // Register the server function before starting the server - DoubleServer::register().unwrap(); - tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 5e7da25ca..3a64f8570 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"] [dependencies] # server functions -server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", default-features = false, features = ["stable"] } +server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "15a4e54435eb5a539afb75891292bcccd2cc8e85", default-features = false, features = ["stable"] } dioxus_server_macro = { path = "server-macro" } # warp diff --git a/packages/fullstack/README.md b/packages/fullstack/README.md index ec3d1c833..ecd003ee8 100644 --- a/packages/fullstack/README.md +++ b/packages/fullstack/README.md @@ -46,7 +46,6 @@ fn main() { ); #[cfg(feature = "ssr")] { - GetMeaning::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/axum-desktop/src/server.rs b/packages/fullstack/examples/axum-desktop/src/server.rs index 7602d1e48..6098ae3f4 100644 --- a/packages/fullstack/examples/axum-desktop/src/server.rs +++ b/packages/fullstack/examples/axum-desktop/src/server.rs @@ -8,9 +8,6 @@ use dioxus_fullstack::prelude::*; #[tokio::main] async fn main() { - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); axum::Server::bind(&addr) .serve( diff --git a/packages/fullstack/examples/axum-hello-world/src/main.rs b/packages/fullstack/examples/axum-hello-world/src/main.rs index 0301a190c..66c3af096 100644 --- a/packages/fullstack/examples/axum-hello-world/src/main.rs +++ b/packages/fullstack/examples/axum-hello-world/src/main.rs @@ -32,8 +32,6 @@ fn main() { true })); - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/axum-router/src/main.rs b/packages/fullstack/examples/axum-router/src/main.rs index 96567c791..2b98ad842 100644 --- a/packages/fullstack/examples/axum-router/src/main.rs +++ b/packages/fullstack/examples/axum-router/src/main.rs @@ -34,8 +34,6 @@ fn main() { })); use axum::extract::State; - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/salvo-hello-world/src/main.rs b/packages/fullstack/examples/salvo-hello-world/src/main.rs index 0cbd3598f..01b67ff99 100644 --- a/packages/fullstack/examples/salvo-hello-world/src/main.rs +++ b/packages/fullstack/examples/salvo-hello-world/src/main.rs @@ -33,8 +33,6 @@ fn main() { })); use salvo::prelude::*; - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/examples/warp-hello-world/src/main.rs b/packages/fullstack/examples/warp-hello-world/src/main.rs index 5e4c9aecb..34fc24c77 100644 --- a/packages/fullstack/examples/warp-hello-world/src/main.rs +++ b/packages/fullstack/examples/warp-hello-world/src/main.rs @@ -32,8 +32,6 @@ fn main() { true })); - PostServerData::register().unwrap(); - GetServerData::register().unwrap(); tokio::runtime::Runtime::new() .unwrap() .block_on(async move { diff --git a/packages/fullstack/server-macro/Cargo.toml b/packages/fullstack/server-macro/Cargo.toml index 286481bdd..cb039057b 100644 --- a/packages/fullstack/server-macro/Cargo.toml +++ b/packages/fullstack/server-macro/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] quote = "1.0.26" -server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] } -syn = { version = "1", features = ["full"] } +server_fn_macro = { git = "https://github.com/leptos-rs/leptos", rev = "15a4e54435eb5a539afb75891292bcccd2cc8e85", features = ["stable"] } +syn = { version = "2", features = ["full"] } [lib] proc-macro = true diff --git a/packages/fullstack/server-macro/src/lib.rs b/packages/fullstack/server-macro/src/lib.rs index f6df526c1..9a1a2c9c7 100644 --- a/packages/fullstack/server-macro/src/lib.rs +++ b/packages/fullstack/server-macro/src/lib.rs @@ -35,7 +35,6 @@ use server_fn_macro::*; /// ``` /// /// Note the following: -/// - You must **register** the server function by calling `T::register()` somewhere in your main function. /// - **Server functions must be `async`.** Even if the work being done inside the function body /// can run synchronously on the server, from the client’s perspective it involves an asynchronous /// function call. @@ -62,6 +61,7 @@ pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { match server_macro_impl( args.into(), s.into(), + syn::parse_quote!(::dioxus_fullstack::prelude::ServerFnTraitObj), Some(context), Some(syn::parse_quote!(::dioxus_fullstack::prelude::server_fn)), ) { diff --git a/packages/fullstack/src/adapters/axum_adapter.rs b/packages/fullstack/src/adapters/axum_adapter.rs index c634d7f77..1ff136d4d 100644 --- a/packages/fullstack/src/adapters/axum_adapter.rs +++ b/packages/fullstack/src/adapters/axum_adapter.rs @@ -12,7 +12,6 @@ //! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); //! #[cfg(feature = "ssr")] //! { -//! GetServerData::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { @@ -107,7 +106,7 @@ pub trait DioxusRouterExt { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - handler: impl Fn(ServerFunction) -> H, + handler: impl FnMut(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler, @@ -232,7 +231,7 @@ where fn register_server_fns_with_handler( self, server_fn_route: &'static str, - mut handler: impl FnMut(ServerFunction) -> H, + mut handler: impl FnMut(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler, @@ -243,7 +242,7 @@ where for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - match func.encoding { + match func.encoding() { Encoding::Url | Encoding::Cbor => { router = router.route(&full_route, post(handler(func))); } @@ -314,7 +313,7 @@ where let cfg = cfg.into(); // Add server functions and render index.html - self.serve_static_assets(&cfg.assets_path) + self.serve_static_assets(cfg.assets_path) .route( "/", get(render_handler).with_state((cfg, SSRState::default())), @@ -371,7 +370,7 @@ async fn render_handler( /// A default handler for server functions. It will deserialize the request, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: DioxusServerContext, - function: ServerFunction, + function: server_fn::ServerFnTraitObj, parts: Arc, body: Body, ) -> impl IntoResponse { @@ -389,11 +388,11 @@ pub async fn server_fn_handler( .expect("couldn't spawn runtime") .block_on(async { let query = &query_string.into(); - let data = match &function.encoding { + let data = match &function.encoding() { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => query, }; - let resp = match (function.trait_obj)(server_context.clone(), &data).await { + let resp = match function.call(server_context.clone(), data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response let accept_header = parts diff --git a/packages/fullstack/src/adapters/salvo_adapter.rs b/packages/fullstack/src/adapters/salvo_adapter.rs index 94814fea0..c2968ff75 100644 --- a/packages/fullstack/src/adapters/salvo_adapter.rs +++ b/packages/fullstack/src/adapters/salvo_adapter.rs @@ -12,7 +12,6 @@ //! #[cfg(feature = "ssr")] //! { //! use salvo::prelude::*; -//! GetServerData::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { @@ -110,7 +109,7 @@ pub trait DioxusRouterExt { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - handler: impl Fn(ServerFunction) -> H, + handler: impl Fn(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler + 'static; @@ -204,7 +203,7 @@ impl DioxusRouterExt for Router { fn register_server_fns_with_handler( self, server_fn_route: &'static str, - mut handler: impl FnMut(ServerFunction) -> H, + mut handler: impl FnMut(server_fn::ServerFnTraitObj) -> H, ) -> Self where H: Handler + 'static, @@ -213,7 +212,7 @@ impl DioxusRouterExt for Router { for server_fn_path in DioxusServerFnRegistry::paths_registered() { let func = DioxusServerFnRegistry::get(server_fn_path).unwrap(); let full_route = format!("{server_fn_route}/{server_fn_path}"); - match func.encoding { + match func.encoding() { Encoding::Url | Encoding::Cbor => { router = router.push(Router::with_path(&full_route).post(handler(func))); } @@ -280,7 +279,7 @@ impl DioxusRouterExt for Router { ) -> Self { let cfg = cfg.into(); - self.serve_static_assets(&cfg.assets_path) + self.serve_static_assets(cfg.assets_path) .connect_hot_reload() .register_server_fns(server_fn_path) .push(Router::with_path("/").get(SSRHandler { cfg })) @@ -288,8 +287,8 @@ impl DioxusRouterExt for Router { fn connect_hot_reload(self) -> Self { let mut _dioxus_router = Router::with_path("_dioxus"); - _dioxus_router = _dioxus_router - .push(Router::with_path("hot_reload").handle(HotReloadHandler::default())); + _dioxus_router = + _dioxus_router.push(Router::with_path("hot_reload").handle(HotReloadHandler)); #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] { _dioxus_router = _dioxus_router.push(Router::with_path("disconnect").handle(ignore_ws)); @@ -346,12 +345,15 @@ impl Handler for SSRHandler /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub struct ServerFnHandler { server_context: DioxusServerContext, - function: ServerFunction, + function: server_fn::ServerFnTraitObj, } impl ServerFnHandler { /// Create a new server function handler with the given server context and server function. - pub fn new(server_context: impl Into, function: ServerFunction) -> Self { + pub fn new( + server_context: impl Into, + function: server_fn::ServerFnTraitObj, + ) -> Self { let server_context = server_context.into(); Self { server_context, @@ -395,11 +397,11 @@ impl ServerFnHandler { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async move { - let data = match &function.encoding { + let data = match function.encoding() { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => &query, }; - let resp = (function.trait_obj)(server_context, data).await; + let resp = function.call(server_context, data).await; resp_tx.send(resp).unwrap(); }) diff --git a/packages/fullstack/src/adapters/warp_adapter.rs b/packages/fullstack/src/adapters/warp_adapter.rs index 872a94cdd..b9f23ae7f 100644 --- a/packages/fullstack/src/adapters/warp_adapter.rs +++ b/packages/fullstack/src/adapters/warp_adapter.rs @@ -11,7 +11,6 @@ //! dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); //! #[cfg(feature = "ssr")] //! { -//! GetServerData::register().unwrap(); //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { @@ -93,7 +92,7 @@ pub fn register_server_fns_with_handler( mut handler: H, ) -> BoxedFilter<(R,)> where - H: FnMut(String, ServerFunction) -> F, + H: FnMut(String, server_fn::ServerFnTraitObj) -> F, F: Filter + Send + Sync + 'static, F::Extract: Send, R: Reply + 'static, @@ -104,7 +103,7 @@ where let full_route = format!("{server_fn_route}/{server_fn_path}") .trim_start_matches('/') .to_string(); - let route = handler(full_route, func.clone()).boxed(); + let route = handler(full_route, func).boxed(); if let Some(boxed_filter) = filter.take() { filter = Some(boxed_filter.or(route).unify().boxed()); } else { @@ -128,7 +127,7 @@ where /// ``` pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl Reply,)> { register_server_fns_with_handler(server_fn_route, |full_route, func| { - path(full_route.clone()) + path(full_route) .and(warp::post().or(warp::get()).unify()) .and(request_parts()) .and(warp::body::bytes()) @@ -250,7 +249,7 @@ impl warp::reject::Reject for RecieveFailed {} /// A default handler for server functions. It will deserialize the request body, call the server function, and serialize the response. pub async fn server_fn_handler( server_context: impl Into, - function: ServerFunction, + function: server_fn::ServerFnTraitObj, parts: RequestParts, body: Bytes, ) -> Result, warp::Rejection> { @@ -274,11 +273,11 @@ pub async fn server_fn_handler( .as_bytes() .to_vec() .into(); - let data = match &function.encoding { + let data = match function.encoding() { Encoding::Url | Encoding::Cbor => &body, Encoding::GetJSON | Encoding::GetCBOR => &query, }; - let resp = match (function.trait_obj)(server_context.clone(), data).await { + let resp = match function.call(server_context.clone(), data).await { Ok(serialized) => { // if this is Accept: application/json then send a serialized JSON response let accept_header = parts @@ -373,7 +372,7 @@ pub fn connect_hot_reload() -> impl Filter impl Filter); impl Drop for DisconnectOnDrop { fn drop(&mut self) { - let _ = self.0.take().unwrap().close(); + std::mem::drop(self.0.take().unwrap().close()); } } diff --git a/packages/fullstack/src/lib.rs b/packages/fullstack/src/lib.rs index be437bc53..18cde9adb 100644 --- a/packages/fullstack/src/lib.rs +++ b/packages/fullstack/src/lib.rs @@ -34,7 +34,7 @@ pub mod prelude { #[cfg(feature = "ssr")] pub use crate::server_context::RequestParts; pub use crate::server_context::{DioxusServerContext, HasServerContext}; - pub use crate::server_fn::ServerFn; + pub use crate::server_fn::DioxusServerFn; #[cfg(feature = "ssr")] pub use crate::server_fn::{ServerFnTraitObj, ServerFunction}; pub use dioxus_server_macro::*; diff --git a/packages/fullstack/src/server_fn.rs b/packages/fullstack/src/server_fn.rs index da7311263..231285149 100644 --- a/packages/fullstack/src/server_fn.rs +++ b/packages/fullstack/src/server_fn.rs @@ -1,30 +1,76 @@ use crate::server_context::DioxusServerContext; #[cfg(any(feature = "ssr", doc))] +#[derive(Clone)] /// A trait object for a function that be called on serializable arguments and returns a serializable result. -pub type ServerFnTraitObj = server_fn::ServerFnTraitObj; +pub struct ServerFnTraitObj(server_fn::ServerFnTraitObj); + +#[cfg(any(feature = "ssr", doc))] +impl std::ops::Deref for ServerFnTraitObj { + type Target = server_fn::ServerFnTraitObj; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(any(feature = "ssr", doc))] +impl std::ops::DerefMut for ServerFnTraitObj { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(any(feature = "ssr", doc))] +impl ServerFnTraitObj { + fn new( + prefix: &'static str, + url: &'static str, + encoding: server_fn::Encoding, + run: ServerFunction, + ) -> Self { + Self(server_fn::ServerFnTraitObj::new(prefix, url, encoding, run)) + } + + /// Create a new `ServerFnTraitObj` from a `server_fn::ServerFnTraitObj`. + pub const fn from_generic_server_fn( + server_fn: server_fn::ServerFnTraitObj, + ) -> Self { + Self(server_fn) + } +} + +#[cfg(any(feature = "ssr", doc))] +server_fn::inventory::collect!(ServerFnTraitObj); #[cfg(any(feature = "ssr", doc))] /// A server function that can be called on serializable arguments and returns a serializable result. -pub type ServerFunction = server_fn::ServerFunction; +pub type ServerFunction = server_fn::SerializedFnTraitObj; #[cfg(any(feature = "ssr", doc))] #[allow(clippy::type_complexity)] static REGISTERED_SERVER_FUNCTIONS: once_cell::sync::Lazy< - std::sync::Arc>>, -> = once_cell::sync::Lazy::new(Default::default); + std::sync::Arc>>, +> = once_cell::sync::Lazy::new(|| { + let mut map = std::collections::HashMap::new(); + for server_fn in server_fn::inventory::iter:: { + map.insert(server_fn.0.url(), server_fn.clone()); + } + std::sync::Arc::new(std::sync::RwLock::new(map)) +}); #[cfg(any(feature = "ssr", doc))] /// The registry of all Dioxus server functions. pub struct DioxusServerFnRegistry; -#[cfg(any(feature = "ssr"))] +#[cfg(feature = "ssr")] impl server_fn::ServerFunctionRegistry for DioxusServerFnRegistry { type Error = ServerRegistrationFnError; - fn register( + fn register_explicit( + prefix: &'static str, url: &'static str, - server_function: std::sync::Arc, + server_function: ServerFunction, encoding: server_fn::Encoding, ) -> Result<(), Self::Error> { // store it in the hashmap @@ -33,10 +79,7 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe .map_err(|e| ServerRegistrationFnError::Poisoned(e.to_string()))?; let prev = write.insert( url, - ServerFunction { - trait_obj: server_function, - encoding, - }, + ServerFnTraitObj::new(prefix, url, encoding, server_function), ); // if there was already a server function with this key, @@ -53,27 +96,32 @@ impl server_fn::ServerFunctionRegistry for DioxusServerFnRe } } - /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. - fn get(url: &str) -> Option { - REGISTERED_SERVER_FUNCTIONS - .read() - .ok() - .and_then(|fns| fns.get(url).cloned()) + fn register( + url: &'static str, + server_function: ServerFunction, + encoding: server_fn::Encoding, + ) -> Result<(), Self::Error> { + Self::register_explicit("", url, server_function, encoding) } /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. - fn get_trait_obj(url: &str) -> Option> { + fn get(url: &str) -> Option> { REGISTERED_SERVER_FUNCTIONS .read() .ok() - .and_then(|fns| fns.get(url).map(|f| f.trait_obj.clone())) + .and_then(|fns| fns.get(url).map(|inner| inner.0.clone())) + } + + /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL. + fn get_trait_obj(url: &str) -> Option> { + Self::get(url) } fn get_encoding(url: &str) -> Option { REGISTERED_SERVER_FUNCTIONS .read() .ok() - .and_then(|fns| fns.get(url).map(|f| f.encoding.clone())) + .and_then(|fns| fns.get(url).map(|f| f.encoding())) } /// Returns a list of all registered server functions. @@ -103,16 +151,16 @@ pub enum ServerRegistrationFnError { /// /// Server functions are created using the `server` macro. /// -/// The function should be registered by calling `ServerFn::register()`. The set of server functions +/// The set of server functions /// can be queried on the server for routing purposes by calling [server_fn::ServerFunctionRegistry::get]. /// /// Technically, the trait is implemented on a type that describes the server function's arguments, not the function itself. -pub trait ServerFn: server_fn::ServerFn { +pub trait DioxusServerFn: server_fn::ServerFn { /// Registers the server function, allowing the client to query it by URL. #[cfg(any(feature = "ssr", doc))] - fn register() -> Result<(), server_fn::ServerFnError> { - Self::register_in::() + fn register_explicit() -> Result<(), server_fn::ServerFnError> { + Self::register_in_explicit::() } } -impl ServerFn for T where T: server_fn::ServerFn {} +impl DioxusServerFn for T where T: server_fn::ServerFn {} From da44b7a3f250b86473aed958a277e4d390467c15 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 19 Jun 2023 16:11:59 -0500 Subject: [PATCH 493/516] fix salvo server function handler example --- packages/fullstack/src/adapters/salvo_adapter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fullstack/src/adapters/salvo_adapter.rs b/packages/fullstack/src/adapters/salvo_adapter.rs index c2968ff75..59b7c36ea 100644 --- a/packages/fullstack/src/adapters/salvo_adapter.rs +++ b/packages/fullstack/src/adapters/salvo_adapter.rs @@ -76,7 +76,7 @@ pub trait DioxusRouterExt { /// use dioxus_fullstack::prelude::*; /// /// struct ServerFunctionHandler { - /// server_fn: ServerFunction, + /// server_fn: server_fn::ServerFnTraitObj, /// } /// /// #[handler] From d93d11262db056572a9010727ecc2851c1463cf1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 20 Jun 2023 11:57:24 -0400 Subject: [PATCH 494/516] bump interpreter version --- packages/interpreter/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index b34dad992..07f5cd930 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-interpreter-js" -version = "0.3.2" +version = "0.3.3" edition = "2018" authors = ["Jonathan Kelley"] description = "JS Intepreter for Dioxus - a concurrent renderer-agnostic Virtual DOM for interactive user experiences" From a3654fd0b865a7b831c0c870b980960eb563ec70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Tue, 20 Jun 2023 19:13:34 +0200 Subject: [PATCH 495/516] Update use_shared_state.rs --- packages/hooks/src/use_shared_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs index eb836a211..3a3a4d639 100644 --- a/packages/hooks/src/use_shared_state.rs +++ b/packages/hooks/src/use_shared_state.rs @@ -71,7 +71,7 @@ impl ProvidedStateInner { /// let current_theme = *theme.read(); /// /// render! { -/// match &*theme.read() { +/// match current_theme { /// Theme::Dark => { /// "Dark mode" /// } From e244159661e288ace0cf61d9282891b3ebd43b2f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 21 Jun 2023 14:58:03 -0700 Subject: [PATCH 496/516] add missing items to Dioxus.toml documentation --- docs/src/configure.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/src/configure.md b/docs/src/configure.md index 95408580c..30c390033 100644 --- a/docs/src/configure.md +++ b/docs/src/configure.md @@ -24,6 +24,7 @@ We use `toml` to define some info for `dioxus` project. ``` asset_dir = "public" ``` +5. ***sub_package*** - the sub package in the workspace to build ### Application.Tools @@ -58,6 +59,11 @@ Web platform application config: # HTML title tag content title = "dioxus app | ⛺" ``` +2. ***base_path*** - The base path to build the appliation for serving at. This can be useful when serving your application in a subdirectory in a domain. For example when building a site to be served on github pages. + ``` + # The application will be served at domain.com/my_application/ + base_path = "my_application" + ``` ### Web.Watcher @@ -72,6 +78,10 @@ Web platform `dev-server` watcher config: ``` watch_path = ["src", "public"] ``` +3. ***index_on_404*** - If enabled, Dioxus CLI will serve the root page when a route is not found. *This is needed when serving an application that uses the router* + ``` + index_on_404 = true + ``` ### Web.Resource From b242afc17b4a1d48f5da6f041d7dd3cb39db7205 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 21 Jun 2023 15:42:48 -0700 Subject: [PATCH 497/516] clean up some wording in the documentation --- docs/src/cmd/README.md | 6 +- docs/src/cmd/build.md | 10 ++-- docs/src/cmd/clean.md | 6 +- docs/src/cmd/serve.md | 38 +++++-------- docs/src/cmd/translate.md | 25 +++++--- docs/src/configure.md | 116 +++++++++++++++++++++----------------- docs/src/creating.md | 29 +++++----- docs/src/installation.md | 12 ++-- 8 files changed, 126 insertions(+), 116 deletions(-) diff --git a/docs/src/cmd/README.md b/docs/src/cmd/README.md index c642b475f..dae226577 100644 --- a/docs/src/cmd/README.md +++ b/docs/src/cmd/README.md @@ -2,7 +2,7 @@ In this chapter we will introduce all `dioxus-cli` commands. -> you can also use `dioxus --help` to get cli help info. +> You can also use `dioxus --help` to get cli help info. ``` dioxus @@ -16,11 +16,11 @@ OPTIONS: -v Enable verbose logging SUBCOMMANDS: - build Build the Rust WASM app and all of its assets + build Build the Dioxus application and all of its assets clean Clean output artifacts config Dioxus config file controls create Init a new project for Dioxus help Print this message or the help of the given subcommand(s) serve Build, watch & serve the Rust WASM app and all of its assets - translate Translate some source file into Dioxus code + translate Translate some html file into a Dioxus component ``` \ No newline at end of file diff --git a/docs/src/cmd/build.md b/docs/src/cmd/build.md index e81973f2d..1fb892475 100644 --- a/docs/src/cmd/build.md +++ b/docs/src/cmd/build.md @@ -15,7 +15,7 @@ OPTIONS: --release [default: false] ``` -You can use this command to build project to `out_dir` : +You can use this command to build a project: ``` dioxus build --release @@ -23,14 +23,14 @@ dioxus build --release ## Target platform -Use option `platform` choose build target platform: +Use the `platform` option to choose your target platform: ``` # for desktop project dioxus build --platform desktop ``` -`platform` only supports `desktop` & `web`. +`platform` currently only supports `desktop` & `web`. ``` # for web project @@ -39,9 +39,9 @@ dioxus build --platform web ## Build Example -You can use `--example {name}` to build a example code. +You can use the `example` option to select a example to build: ``` -# build `example/test` code +# build the `test` example dioxus build --exmaple test ``` \ No newline at end of file diff --git a/docs/src/cmd/clean.md b/docs/src/cmd/clean.md index 2f889c1a8..bbb3a132e 100644 --- a/docs/src/cmd/clean.md +++ b/docs/src/cmd/clean.md @@ -1,16 +1,16 @@ # Clean -`dioxus clean` will call `target clean` and remove `out_dir` directory. +`dioxus clean` will clear the build artifacts (the out_dir and the cargo cache) ``` dioxus-clean -Clean output artifacts +Clean build artifacts USAGE: dioxus clean ``` -you can use this command to clean all build cache and the `out_dir` content. +# Example ``` dioxus clean diff --git a/docs/src/cmd/serve.md b/docs/src/cmd/serve.md index dc2d14593..41abf5350 100644 --- a/docs/src/cmd/serve.md +++ b/docs/src/cmd/serve.md @@ -1,6 +1,6 @@ # Serve -The `dioxus serve` can start a dev server (include hot-reload tool) to run the project. +The `dioxus serve` can start a dev server with hot-reloading ``` dioxus-serve @@ -13,53 +13,43 @@ OPTIONS: --example [default: ""] --platform [default: "default_platform"] --release [default: false] + --hot-reload [default: false]ß ``` -You can use this command to build project and start a `dev server` : +You can use this command to build project and start a dev server: ``` dioxus serve ``` -## Target platform - -Use option `platform` choose build target platform: - -``` -# for desktop project -dioxus serve --platform desktop -``` - -`platform` only supports `desktop` & `web`. - -`dev-server` only for `web` project. - -``` -# for web project -dioxus serve --platform web -``` - ## Serve Example -You can use `--example {name}` to start a example code. +You can use the `example` option to serve a example: ``` -# build `example/test` code +# serve the `test` example dioxus serve --exmaple test ``` ## Open Browser -You can add `--open` flag to open system default browser when server startup. +You can add the `--open` option to open system default browser when server startup: ``` dioxus serve --open ``` +## RSX Hot Reloading + +You can add the `--hot-reload` flag to enable [rsx hot reloading](https://dioxuslabs.com/docs/0.3/guide/en/getting_started/hot_reload.html). This will allow you to reload some rsx changes without a full recompile: + +``` +dioxus serve --open +``` ## Cross Origin Policy -use `cross-origin-policy` can change corss-origin header in serverside. +You can add the `cross-origin-policy` option to change cross-origin header to: ``` Cross-Origin-Opener-Policy: same-origin diff --git a/docs/src/cmd/translate.md b/docs/src/cmd/translate.md index 415a88fba..7237e1dcf 100644 --- a/docs/src/cmd/translate.md +++ b/docs/src/cmd/translate.md @@ -1,16 +1,16 @@ # Translate -`dioxus translate` can translate some source file into Dioxus code. +`dioxus translate` can translate some `html` file into a Dioxus compoent ``` dioxus-translate -Translate some source file into Dioxus code +Translate some source file into a Dioxus component USAGE: dioxus translate [OPTIONS] [OUTPUT] ARGS: - Output file, stdout if not present + Output file, defaults to stdout if not present OPTIONS: -c, --component Activate debug mode @@ -19,20 +19,31 @@ OPTIONS: ## Translate HTML to stdout +You can use the `file` option to set path to the `html` file to translate: + ``` dioxus transtale --file ./index.html ``` -## Output in a file +## Output rsx to a file + +You can pass a file to the traslate command to set the path to write the output of the command to: ``` -dioxus translate --component --file ./index.html component.rsx +dioxus translate --file ./index.html component.rsx ``` -set `component` flag will wrap `dioxus rsx` code in a component function. +## Output rsx to a file + +Setting the `component` option will create a compoent from the HTML: + +``` +dioxus translate --file ./index.html --component +``` ## Example +This HTML: ```html

Hello World

@@ -40,7 +51,7 @@ set `component` flag will wrap `dioxus rsx` code in a component function.
``` -Translate HTML to Dioxus component code. +Translates into this Dioxus component: ```rust fn component(cx: Scope) -> Element { diff --git a/docs/src/configure.md b/docs/src/configure.md index 30c390033..25a5a365a 100644 --- a/docs/src/configure.md +++ b/docs/src/configure.md @@ -1,80 +1,78 @@ # Configure Project -This chapter will introduce `Dioxus.toml` and anatomy the config file. +This chapter will introduce you to how to configure the CLI with your `Dioxus.toml` file ## Structure -We use `toml` to define some info for `dioxus` project. +The CLI uses a `Dioxus.toml` file in the root of your crate to define some configuration for your `dioxus` project. ### Application -1. ***name*** - project name & title -2. ***default_platform*** - which platform target for this project. +General application confiration: + +``` +[application] +# configuration +``` + +1. ***name*** - Project name ``` - # current support: web, desktop + name = "my-project" + ``` +2. ***default_platform*** - The platform this project targets + ```ß + # current supported platforms: web, desktop # default: web default_platform = "web" ``` - change this to `desktop`, the `dioxus build & serve` will default build desktop app. -3. ***out_dir*** - which directory to put the output file; use `dioxus build & service`, the output will put at this directory, and the `assets` will be also copy to here. + if you change this to `desktop`, the `dioxus build` will default building a desktop app +3. ***out_dir*** - The directory to place the build artifacts from `dioxus build` or `dioxus service` into. This is also where the `assets` directory will be copied to ``` out_dir = "dist" ``` -4. ***asset_dir*** - which direcotry to put your `static, assets` file, cli will automatic copy all file to `out_dir`, so you can put some resource file in there, like `CSS, JS, Image` file. +4. ***asset_dir*** - The directory with your static assets. The CLI will automatically copy these assets into the ***out_dir*** after a build/serve. ``` asset_dir = "public" ``` -5. ***sub_package*** - the sub package in the workspace to build - -### Application.Tools - -You can combine different tools with `dioxus`. - -1. ***binaryen*** - Use the `binaryen` tooling suite. +5. ***sub_package*** - The sub package in the workspace to build by default ``` - # current support: wasm-opt - # default: web - binaryen = { wasm_opt = true } + sub_package = "my-crate" ``` - 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. ***tailwindcss*** - Use the `tailwindcss` standalone binary to generate a Tailwind CSS bundle file. - ``` - tailwindcss = { 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 `tailwindcss` with the `--minify` option. - - Note : Dioxus will automatically include the generated tailwind file in the `index.html` ### Web.App -Web platform application config: +Configeration specific to web applications: -1. ***title*** - this value will display on the web page title. like `` tag. +``` +[web.app] +# configuration +``` + +1. ***title*** - The title of the web page ``` # HTML title tag content title = "dioxus app | ⛺" ``` -2. ***base_path*** - The base path to build the appliation for serving at. This can be useful when serving your application in a subdirectory in a domain. For example when building a site to be served on github pages. +2. ***base_path*** - The base path to build the appliation for serving at. This can be useful when serving your application in a subdirectory under a domain. For example when building a site to be served on github pages. ``` - # The application will be served at domain.com/my_application/ + # The application will be served at domain.com/my_application/, so we need to modify the base_path to the path where the application will be served base_path = "my_application" ``` ### Web.Watcher -Web platform `dev-server` watcher config: +Configeration related to the development server: -1. ***reload_html*** - a boolean value; when watcher trigger, regenerate `index.html` file. +``` +[web.watcher] +# configuration +``` + +1. ***reload_html*** - If this is true, the cli will rebuild the index.html file every time the application is rebuilt ``` - # when watcher trigger, regenerate the `index.html` reload_html = true ``` -2. ***watch_path*** - which files & directories will be watcher monitoring. +2. ***watch_path*** - The files & directories to moniter for changes ``` watch_path = ["src", "public"] ``` @@ -85,9 +83,13 @@ Web platform `dev-server` watcher config: ### Web.Resource -Include some `CSS Javascript` resources into html file. +Configeration related to static resources your application uses: +``` +[web.resource] +# configuration +``` -1. ***style*** - include some style(CSS) file into html. +1. ***style*** - The styles (`.css` files) to include in your application ``` style = [ # include from public_dir. @@ -96,7 +98,7 @@ Include some `CSS Javascript` resources into html file. "https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.css" ] ``` -2. ***script*** - include some script(JS) file into html. +2. ***script*** - The additional scripts (`.js` files) to include in your application ``` style = [ # include from public_dir. @@ -108,9 +110,13 @@ Include some `CSS Javascript` resources into html file. ### Web.Resource.Dev -Only include resources at `Dev` mode. +Configeration related to static resources your application uses in development: +``` +[web.resource.dev] +# configuration +``` -1. ***style*** - include some style(CSS) file into html. +1. ***style*** - The styles (`.css` files) to include in your application ``` style = [ # include from public_dir. @@ -119,7 +125,7 @@ Only include resources at `Dev` mode. "https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.css" ] ``` -2. ***script*** - include some script(JS) file into html. +2. ***script*** - The additional scripts (`.js` files) to include in your application ``` style = [ # include from public_dir. @@ -131,9 +137,14 @@ Only include resources at `Dev` mode. ### Web.Proxy -Proxy requests matching a path to a backend server. +Configeration related to any proxies your application requires durring development. Proxies will forward requests to a new service -1. ***backend*** - the URL to the backend server. +``` +[web.proxy] +# configuration +``` + +1. ***backend*** - The URL to the server to proxy. The CLI will forward any requests under the backend relative route to the backend instead of returning 404 ``` backend = "http://localhost:8000/api/" ``` @@ -147,14 +158,13 @@ Proxy requests matching a path to a backend server. # App (Project) Name name = "{{project-name}}" -# Dioxus App Default Platform -# desktop, web, mobile, ssr +# The Dioxus platform to default to default_platform = "web" -# `build` & `serve` dist path +# `build` & `serve` output path out_dir = "dist" -# resource (public) file folder +# the static resource path asset_dir = "public" [web.app] @@ -164,10 +174,10 @@ title = "dioxus | ⛺" [web.watcher] -# when watcher trigger, regenerate the `index.html` +# when watcher is triggered, regenerate the `index.html` reload_html = true -# which files or dirs will be watcher monitoring +# which files or dirs will be monitored watch_path = ["src", "public"] # include `assets` in web platform diff --git a/docs/src/creating.md b/docs/src/creating.md index fa725a4f7..5bb98fd10 100644 --- a/docs/src/creating.md +++ b/docs/src/creating.md @@ -4,35 +4,36 @@ Once you have the Dioxus CLI tool installed, you can use it to create dioxus pro ## Initializing a default project -The `dioxus create` command will create a new directory containing a project template. +First, run the `dioxus create` command to create a new project ready to be used with Dioxus and the Dioxus CLI: + ``` dioxus create hello-dioxus ``` -It will clone a default template from github template: [DioxusLabs/dioxus-template](https://github.com/DioxusLabs/dioxus-template) - +> It will clone a default template from github template: [DioxusLabs/dioxus-template](https://github.com/DioxusLabs/dioxus-template) > This default template is use for `web` platform application. +> +> You can choose to create your project from a different template by passing the `template` argument: +> ``` +> dioxus init hello-dioxus --template=gh:dioxuslabs/dioxus-template +> ``` -then you can change the current directory in to the project: +Next, move the current directory into your new project: ``` cd hello-dioxus ``` > Make sure `wasm32 target` is installed before running the Web project. +> You can install the wasm target for rust using rustup: +> ``` +> rustup target add wasm32-unknown-unknown +> ``` -now we can create a `dev server` to display the project: +Finally, create serve your project with the Dioxus CLI: ``` dioxus serve ``` -by default, the dioxus dev server will running at: [`http://127.0.0.1:8080/`](http://127.0.0.1:8080/) - -## Initalizing from other repository - -you can assign which repository you want to create: - -``` -dioxus init hello-dioxus --template=gh:dioxuslabs/dioxus-template -``` \ No newline at end of file +By default, the CLI serve your site at: [`http://127.0.0.1:8080/`](http://127.0.0.1:8080/) diff --git a/docs/src/installation.md b/docs/src/installation.md index 7151de1ff..b4326d296 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -1,12 +1,10 @@ # Installation -There are multiple ways to install the Dioxus CLI tool. Choose any one of the methods below that best suit your needs. +Choose any one of the methods below to install the Dioxus CLI: -## Install from latest master version +## Install from latest git version -We suggest you use `github master` version to install it now. - -We have not yet released the latest version to `crates.io` +To get the most up to date bug fixes and features of the Dioxus CLI, you can install the development version from git. ``` cargo install --git https://github.com/Dioxuslabs/cli @@ -17,8 +15,8 @@ and install it in Cargo's global binary directory (`~/.cargo/bin/` by default). ## Install from `crates.io` version +The published version of the Dioxus CLI is updated less often, but should be more stable than the git version of the Dioxus CLI. + ``` cargo install dioxus-cli ``` - -Make sure to add the Cargo bin directory to your `PATH`. \ No newline at end of file From 16ab96aa0a47f4ed7a589b4b4abe940e82225ce6 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 21 Jun 2023 16:07:21 -0700 Subject: [PATCH 498/516] Automatically insert scripts if they are not found --- src/builder.rs | 53 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index d81408740..b5d107046 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -462,7 +462,8 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { { style_str.push_str("\n"); } - html = html.replace("{style_include}", &style_str); + + replace_or_insert_before("{style_include}", &style_str, " String { )) } - html = html.replace("{script_include}", &script_str); + replace_or_insert_before("{script_include}", &script_str, " String { ); } - html = html.replace("{app_name}", &config.application.name); - - html = match &config.web.app.base_path { - Some(path) => html.replace("{base_path}", path), - None => html.replace("{base_path}", "."), + let base_path = match &config.web.app.base_path { + Some(path) => path, + None => ".", }; + let app_name = &config.application.name; + // Check if a script already exists + if html.contains("{app_name}") && html.contains("{base_path}") { + html = html.replace("{app_name}", app_name); + + html = html.replace("{base_path}", base_path); + } else { + // If not, insert the script + html = html.replace( + " + import init from "/{base_path}/assets/dioxus/{app_name}.js"; + init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {{ + if (wasm.__wbindgen_start == undefined) {{ + wasm.main(); + }} + }}); + + String { .clone() .unwrap_or_else(|| "dioxus | ⛺".into()); - html.replace("{app_title}", &title) + replace_or_insert_before("{app_title}", &title, " Date: Wed, 21 Jun 2023 17:27:01 -0700 Subject: [PATCH 499/516] fix some errors not being reported --- src/builder.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index d81408740..c06fb945d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -380,7 +380,17 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result> { ); pb.set_message("💼 Waiting to start build the project..."); - let stdout = cmd.stream_stdout()?; + struct StopSpinOnDrop(ProgressBar); + + impl Drop for StopSpinOnDrop { + fn drop(&mut self) { + self.0.finish_and_clear(); + } + } + + StopSpinOnDrop(pb.clone()); + + let stdout = cmd.detached().stream_stdout()?; let reader = std::io::BufReader::new(stdout); for message in cargo_metadata::Message::parse_stream(reader) { match message.unwrap() { @@ -388,9 +398,11 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result> { let message = msg.message; match message.level { cargo_metadata::diagnostic::DiagnosticLevel::Error => { - return Err(anyhow::anyhow!(message - .rendered - .unwrap_or("Unknown".into()))); + return { + Err(anyhow::anyhow!(message + .rendered + .unwrap_or("Unknown".into()))) + }; } cargo_metadata::diagnostic::DiagnosticLevel::Warning => { warning_messages.push(message.clone()); @@ -407,7 +419,6 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result> { } Message::BuildFinished(finished) => { if finished.success { - pb.finish_and_clear(); log::info!("👑 Build done."); } else { std::process::exit(1); From 1e5c2207363ee0c1fc2f3ff73e8675dffa7dda97 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 24 Jun 2023 10:25:24 -0700 Subject: [PATCH 500/516] expose wry background color option --- packages/desktop/src/cfg.rs | 10 ++++++++++ packages/desktop/src/webview.rs | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index d7f1b5dfa..619a6801a 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -23,6 +23,7 @@ pub struct Config { pub(crate) custom_head: Option, pub(crate) custom_index: Option, pub(crate) root_name: String, + pub(crate) background_color: Option<(u8, u8, u8, u8)>, } type DropHandler = Box bool>; @@ -50,6 +51,7 @@ impl Config { custom_head: None, custom_index: None, root_name: "main".to_string(), + background_color: None, } } @@ -137,6 +139,14 @@ impl Config { self.root_name = name.into(); self } + + /// Sets the background color of the WebView. + /// This will be set before the HTML is rendered and can be used to prevent flashing when the page loads. + /// Accepts a color in RGBA format + pub fn with_background_color(mut self, color: (u8, u8, u8, u8)) -> Self { + self.background_color = Some(color); + self + } } impl Default for Config { diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 5d3acf44f..0b7152cfd 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -64,6 +64,10 @@ pub fn build( webview = webview.with_browser_accelerator_keys(false); } + if let Some(color) = cfg.background_color { + webview = webview.with_background_color(color); + } + // These are commented out because wry is currently broken in wry // let mut web_context = WebContext::new(cfg.data_dir.clone()); // .with_web_context(&mut web_context); From 9fbeb84921434eb34d91472d486cfbd694b54a28 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 26 Jun 2023 08:53:45 -0700 Subject: [PATCH 501/516] update to git dioxus --- Cargo.lock | 53 ++++++++++++++++++++++++++++++++++++++--------- Cargo.toml | 10 ++++----- src/server/mod.rs | 16 ++++++++------ 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 863ae5823..b0a98d3c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,6 +400,15 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cfg-expr" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bbc13bf6290a6b202cc3efb36f7ec2b739a80634215630c8053a313edf6abef" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -727,8 +736,7 @@ dependencies = [ [[package]] name = "dioxus-autofmt" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935ac336dd077436505f2eda7b7b81515093e8a3fb352ac514cd6901dbd302e7" +source = "git+https://github.com/DioxusLabs/dioxus#11c9abcf7ce731ccb4a44c52de383c090ab319af" dependencies = [ "dioxus-rsx", "prettier-please", @@ -795,8 +803,7 @@ dependencies = [ [[package]] name = "dioxus-core" version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4d15b0bb9c58d015b2295f240600dd76e427758377569fa33783afc295706a" +source = "git+https://github.com/DioxusLabs/dioxus#11c9abcf7ce731ccb4a44c52de383c090ab319af" dependencies = [ "bumpalo", "bumpslab", @@ -814,8 +821,7 @@ dependencies = [ [[package]] name = "dioxus-html" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7682a6615e4e5a460cd3293ce420451abffb719c84c4b54e297b17365f601fb4" +source = "git+https://github.com/DioxusLabs/dioxus#11c9abcf7ce731ccb4a44c52de383c090ab319af" dependencies = [ "async-trait", "dioxus-core", @@ -831,11 +837,11 @@ dependencies = [ [[package]] name = "dioxus-rsx" version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8544632e20f462a64f26502c91e7cf6ae3b30d82956e70543644d2c16b6659d" +source = "git+https://github.com/DioxusLabs/dioxus#11c9abcf7ce731ccb4a44c52de383c090ab319af" dependencies = [ "dioxus-core", "internment", + "krates", "proc-macro2", "quote", "serde", @@ -1011,6 +1017,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.26" @@ -1680,6 +1692,18 @@ dependencies = [ "libc", ] +[[package]] +name = "krates" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c43a6cba1c201dfe81a943c89fa5c9140b34993e0c027f542c80b92e319a7" +dependencies = [ + "cargo_metadata", + "cfg-expr", + "petgraph", + "semver", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2180,6 +2204,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -2454,8 +2488,7 @@ dependencies = [ [[package]] name = "rsx-rosetta" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "593810f58a97e3a45e23b84437d5323c2420b46f73cf38fe49133eb59a7524bb" +source = "git+https://github.com/DioxusLabs/dioxus#11c9abcf7ce731ccb4a44c52de383c090ab319af" dependencies = [ "convert_case", "dioxus-autofmt", diff --git a/Cargo.toml b/Cargo.toml index 499f5170e..7dca72934 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,11 +77,11 @@ ctrlc = "3.2.3" # dioxus-rsx = "0.0.1" gitignore = "1.0.7" -dioxus-rsx = { version = "0.0.3" } -dioxus-html = { version = "0.3", features = ["hot-reload-context"] } -dioxus-core = { version = "0.3", features = ["serialize"] } -dioxus-autofmt = "0.3.0" -rsx-rosetta = { version = "0.3" } +dioxus-rsx = { git = "https://github.com/DioxusLabs/dioxus" } +dioxus-html = { git = "https://github.com/DioxusLabs/dioxus", features = ["hot-reload-context"] } +dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", features = ["serialize"] } +dioxus-autofmt = { git = "https://github.com/DioxusLabs/dioxus" } +rsx-rosetta = { git = "https://github.com/DioxusLabs/dioxus" } open = "4.1.0" [[bin]] diff --git a/src/server/mod.rs b/src/server/mod.rs index 8e5861270..4f19f00d2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -150,10 +150,10 @@ pub async fn startup_hot_reload( let dist_path = config.out_dir.clone(); let (reload_tx, _) = broadcast::channel(100); - let map = FileMap::::new(config.crate_dir.clone()); - // for err in errors { - // log::error!("{}", err); - // } + let FileMapBuildResult { map, errors } = FileMap::::create(config.crate_dir.clone()).unwrap(); + for err in errors { + log::error!("{}", err); + } let file_map = Arc::new(Mutex::new(map)); let build_manager = Arc::new(BuildManager { config: config.clone(), @@ -219,10 +219,11 @@ pub async fn startup_hot_reload( let mut map = file_map.lock().unwrap(); match map.update_rsx(&path, &crate_dir) { - UpdateResult::UpdatedRsx(msgs) => { + + Ok( UpdateResult::UpdatedRsx(msgs) )=> { messages.extend(msgs); } - UpdateResult::NeedsRebuild => { + Ok(UpdateResult::NeedsRebuild) => { match build_manager.rebuild() { Ok(res) => { print_console_info( @@ -242,6 +243,9 @@ pub async fn startup_hot_reload( } return; } + Err(err) => { + log::error!("{}", err); + } } } for msg in messages { From 42011b7aa7b27509d130716c7395a537c94cd921 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 26 Jun 2023 13:12:42 -0700 Subject: [PATCH 502/516] improve dioxus create --- Cargo.lock | 1701 ++++++++++++++++++++++++++++---------- Cargo.toml | 4 +- src/cli/cfg.rs | 4 +- src/cli/create/mod.rs | 106 ++- src/cli/mod.rs | 1 - src/cli/translate/mod.rs | 2 +- src/server/mod.rs | 6 +- 7 files changed, 1326 insertions(+), 498 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0a98d3c8..707a02b6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,14 +19,13 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "opaque-debug", ] [[package]] @@ -35,20 +34,51 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "const-random", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.1" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a21b9440a626c7fc8573a9e3d3a06b75c7c97754c2949bc7857b90353ca655" +dependencies = [ + "as-slice", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -64,6 +94,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -73,12 +109,67 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + [[package]] name = "arrayref" version = "0.3.7" @@ -91,6 +182,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "async-compression" version = "0.3.15" @@ -113,7 +213,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -217,9 +317,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -237,7 +337,7 @@ dependencies = [ "dirs 1.0.5", "failure", "flate2", - "hex", + "hex 0.3.2", "is_executable", "siphasher", "tar", @@ -301,10 +401,31 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.12.1" +name = "bstr" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" +dependencies = [ + "memchr", + "once_cell", + "regex-automata", + "serde", +] + +[[package]] +name = "btoi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" +dependencies = [ + "num-traits", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bumpslab" @@ -357,6 +478,43 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-generate" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b2f627381dc7523340c606559dddf6083cb2e6134368381da5778638f906d8" +dependencies = [ + "anyhow", + "clap", + "console", + "dialoguer", + "env_logger", + "git2", + "gix-config", + "heck 0.4.1", + "home", + "ignore", + "indicatif", + "liquid", + "liquid-core", + "liquid-derive", + "liquid-lib", + "log", + "names", + "paste", + "path-absolutize", + "regex", + "remove_dir_all", + "rhai", + "sanitize-filename", + "semver", + "serde", + "tempfile", + "thiserror", + "toml 0.7.5", + "walkdir", +] + [[package]] name = "cargo-platform" version = "0.1.2" @@ -388,7 +546,7 @@ checksum = "e72c3ff59e3b7d24630206bb63a73af65da4ed5df1f76ee84dfafb9fee2ba60e" dependencies = [ "serde", "serde_derive", - "toml", + "toml 0.5.11", ] [[package]] @@ -417,13 +575,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time 0.1.45", "wasm-bindgen", @@ -432,61 +590,61 @@ dependencies = [ [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] name = "clap" -version = "3.2.25" +version = "4.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" dependencies = [ - "atty", - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "indexmap", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.25" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck 0.4.1", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.22", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" @@ -512,15 +670,37 @@ dependencies = [ [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.45.0", +] + +[[package]] +name = "const-random" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "proc-macro-hack", + "tiny-keccak", ] [[package]] @@ -553,9 +733,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -581,13 +761,19 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -600,12 +786,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.5" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" +checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" dependencies = [ "nix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -619,15 +805,15 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "socket2", + "socket2 0.4.9", "winapi", ] [[package]] name = "curl-sys" -version = "0.4.61+curl-8.0.1" +version = "0.4.63+curl-8.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d05c10f541ae6f3bc5b3d923c20001f47db7d5f0b2bc6ad16490133842db79" +checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc" dependencies = [ "cc", "libc", @@ -639,47 +825,12 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.94" +name = "cvt" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", + "cfg-if", ] [[package]] @@ -702,7 +853,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -713,20 +864,32 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "dialoguer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -754,6 +917,7 @@ dependencies = [ "atty", "axum", "binary-install", + "cargo-generate", "cargo_metadata", "cargo_toml", "chrono", @@ -774,7 +938,7 @@ dependencies = [ "headers", "html_parser", "hyper", - "hyper-rustls", + "hyper-rustls 0.23.2", "indicatif", "lazy_static", "log", @@ -792,12 +956,13 @@ dependencies = [ "tar", "thiserror", "tokio", - "toml", + "toml 0.5.11", + "toml_edit", "tower", "tower-http 0.2.5", "walkdir", "wasm-bindgen-cli-support", - "zip 0.6.5", + "zip 0.6.6", ] [[package]] @@ -809,7 +974,7 @@ dependencies = [ "bumpslab", "futures-channel", "futures-util", - "indexmap", + "indexmap 1.9.3", "log", "longest-increasing-subsequence", "rustc-hash", @@ -879,6 +1044,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "either" version = "1.8.1" @@ -914,25 +1085,44 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.13" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b025475ad197bd8b4a9bdce339216b6cf3bd568bf2e107c286b51613f0b3cf" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c2852ff17a4c9a2bb2abbca3074737919cb05dc24b0a8ca9498081a7033dd6" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.3.1" @@ -1056,13 +1246,27 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_at" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15550ecca96ea332ec143fb450701074143b70d358e50b32b1f847ccff2e1cf7" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "libc", + "nix", + "windows-sys 0.48.0", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1134,7 +1338,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -1190,9 +1394,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -1201,9 +1405,24 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "git2" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] [[package]] name = "gitignore" @@ -1214,6 +1433,232 @@ dependencies = [ "glob", ] +[[package]] +name = "gix-actor" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc22b0cdc52237667c301dd7cdc6ead8f8f73c9f824e9942c8ebd6b764f6c0bf" +dependencies = [ + "bstr 1.5.0", + "btoi", + "gix-date", + "itoa", + "nom", + "thiserror", +] + +[[package]] +name = "gix-config" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbad5ce54a8fc997acc50febd89ec80fa6e97cb7f8d0654cb229936407489d8" +dependencies = [ + "bstr 1.5.0", + "gix-config-value", + "gix-features 0.28.1", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "log", + "memchr", + "nom", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-config-value" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09154c0c8677e4da0ec35e896f56ee3e338e741b9599fae06075edd83a4081c" +dependencies = [ + "bitflags", + "bstr 1.5.0", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96271912ce39822501616f177dea7218784e6c63be90d5f36322ff3a722aae2" +dependencies = [ + "bstr 1.5.0", + "itoa", + "thiserror", + "time 0.3.22", +] + +[[package]] +name = "gix-features" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b76f9a80f6dd7be66442ae86e1f534effad9546676a392acc95e269d0c21c22" +dependencies = [ + "gix-hash 0.10.4", + "libc", + "sha1_smol", + "walkdir", +] + +[[package]] +name = "gix-features" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf69b0f5c701cc3ae22d3204b671907668f6437ca88862d355eaf9bc47a4f897" +dependencies = [ + "gix-hash 0.11.3", + "libc", +] + +[[package]] +name = "gix-fs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b37a1832f691fdc09910bd267f9a2e413737c1f9ec68c6e31f9e802616278a9" +dependencies = [ + "gix-features 0.29.0", +] + +[[package]] +name = "gix-glob" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e43efd776bc543f46f0fd0ca3d920c37af71a764a16f2aebd89765e9ff2993" +dependencies = [ + "bitflags", + "bstr 1.5.0", +] + +[[package]] +name = "gix-hash" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a258595457bc192d1f1c59d0d168a1e34e2be9b97a614e14995416185de41a7" +dependencies = [ + "hex 0.4.3", + "thiserror", +] + +[[package]] +name = "gix-hash" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0dd58cdbe7ffa4032fc111864c80d5f8cecd9a2c9736c97ae7e5be834188272" +dependencies = [ + "hex 0.4.3", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c693d7f05730fa74a7c467150adc7cea393518410c65f0672f80226b8111555" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-object" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df068db9180ee935fbb70504848369e270bdcb576b05c0faa8b9fd3b86fc017" +dependencies = [ + "bstr 1.5.0", + "btoi", + "gix-actor", + "gix-features 0.28.1", + "gix-hash 0.10.4", + "gix-validate", + "hex 0.4.3", + "itoa", + "nom", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-path" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32370dce200bb951df013e03dff35b4233fc7a89458642b047629b91734a7e19" +dependencies = [ + "bstr 1.5.0", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e909396ed3b176823991ccc391c276ae2a015e54edaafa3566d35123cfac9d" +dependencies = [ + "gix-actor", + "gix-features 0.28.1", + "gix-hash 0.10.4", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-validate", + "memmap2", + "nom", + "thiserror", +] + +[[package]] +name = "gix-sec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ffa5bf0772f9b01de501c035b6b084cf9b8bb07dec41e3afc6a17336a65f47" +dependencies = [ + "bitflags", + "dirs 4.0.0", + "gix-path", + "libc", + "windows 0.43.0", +] + +[[package]] +name = "gix-tempfile" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71a0d32f34e71e86586124225caefd78dabc605d0486de580d717653addf182" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca284c260845bc0724050aec59c7a596407678342614cdf5a1d69e044f29a36" +dependencies = [ + "fastrand", +] + +[[package]] +name = "gix-validate" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d092b594c8af00a3a31fe526d363ee8a51a6f29d8496cdb991ed2f01ec0ec13" +dependencies = [ + "bstr 1.5.0", + "thiserror", +] + [[package]] name = "glob" version = "0.3.1" @@ -1221,10 +1666,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "h2" -version = "0.3.18" +name = "globset" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick 0.7.20", + "bstr 1.5.0", + "fnv", + "log", + "regex", +] + +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -1232,7 +1690,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -1245,9 +1703,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "headers" version = "0.3.8" @@ -1318,6 +1782,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hmac" version = "0.12.1" @@ -1327,6 +1797,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "hostname" version = "0.3.1" @@ -1393,10 +1872,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] -name = "hyper" -version = "0.14.26" +name = "humantime" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1409,7 +1894,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1425,10 +1910,23 @@ dependencies = [ "http", "hyper", "log", - "rustls", + "rustls 0.20.8", "rustls-native-certs", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.4", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +dependencies = [ + "http", + "hyper", + "rustls 0.21.2", + "tokio", + "tokio-rustls 0.24.1", ] [[package]] @@ -1446,26 +1944,25 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows 0.48.0", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -1493,14 +1990,31 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1508,16 +2022,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] name = "indicatif" -version = "0.17.3" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" +checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" dependencies = [ "console", + "instant", "number_prefix", "portable-atomic", "unicode-width", @@ -1543,6 +2068,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1554,19 +2088,19 @@ dependencies = [ [[package]] name = "internment" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a798d7677f07d6f1e77be484ea8626ddb1566194de399f1206306820c406371" +checksum = "161079c3ad892faa215fcfcf3fd7a6a3c9288df2b06a2c2bad7fbfad4f01d69d" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", "parking_lot", ] [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -1575,14 +2109,14 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.3", "widestring", - "winapi", - "winreg", + "windows-sys 0.48.0", + "winreg 0.50.0", ] [[package]] @@ -1609,6 +2143,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "is-wsl" version = "0.4.0" @@ -1654,9 +2200,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1704,6 +2250,16 @@ dependencies = [ "semver", ] +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "serde", + "static_assertions", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1718,9 +2274,37 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.143" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libgit2-sys" +version = "0.15.2+1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] [[package]] name = "libz-sys" @@ -1734,15 +2318,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1751,15 +2326,72 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "liquid" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f68ae1011499ae2ef879f631891f21c78e309755f4a5e483c4a8f12e10b609" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e0724dfcaad5cfb7965ea0f178ca0870b8d7315178f4a7179f5696f7f04d5f" +dependencies = [ + "anymap2", + "itertools", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time 0.3.22", +] + +[[package]] +name = "liquid-derive" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2fb41a9bb4257a3803154bdf7e2df7d45197d1941c9b1a90ad815231630721" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.22", +] + +[[package]] +name = "liquid-lib" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a17e273a6fb1fb6268f7a5867ddfd0bd4683c7e19b51084f3d567fad4348c0" +dependencies = [ + "itertools", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time 0.3.22", + "unicode-segmentation", +] [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -1767,12 +2399,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "longest-increasing-subsequence" @@ -1791,9 +2420,9 @@ dependencies = [ [[package]] name = "lua-src" -version = "544.0.1" +version = "546.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708ba3c844d5e9d38def4a09dd871c17c370f519b3c4b7261fbabe4a613a814c" +checksum = "8cb00c1380f1b4b4928dd211c07301ffa34872a239e590bd3219d9e5b213face" dependencies = [ "cc", ] @@ -1831,6 +2460,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "mime" version = "0.3.17" @@ -1873,23 +2511,23 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "mlua" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8ce6788556a67d90567809c7de94dfef2ff1f47ff897aeee935bcfbcdf5735" +checksum = "07366ed2cd22a3b000aed076e2b68896fb46f06f1f5786c5962da73c0af01577" dependencies = [ - "bstr", + "bstr 0.2.17", "cc", "futures-core", "futures-task", @@ -1918,6 +2556,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "names" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" +dependencies = [ + "rand", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -1959,10 +2606,19 @@ dependencies = [ ] [[package]] -name = "notify" -version = "5.1.0" +name = "normpath" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "notify" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" dependencies = [ "bitflags", "crossbeam-channel", @@ -1974,17 +2630,7 @@ dependencies = [ "mio", "serde", "walkdir", - "windows-sys 0.42.0", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", + "windows-sys 0.45.0", ] [[package]] @@ -2006,6 +2652,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -2014,40 +2669,35 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.30.3" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "open" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16814a067484415fda653868c9be0ac5f2abd2ef5d951082a5f2fe1b3662944" +checksum = "3a083c0c7e5e4a8ec4176346cf61f67ac674e8bfb059d9226e1c54a96b377c12" dependencies = [ "is-wsl", + "libc", "pathdiff", ] [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if", @@ -2066,7 +2716,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -2077,9 +2727,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -2096,12 +2746,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "parking_lot" version = "0.12.1" @@ -2114,15 +2758,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.0", ] [[package]] @@ -2136,6 +2780,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "path-absolutize" +version = "3.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1d4993b16f7325d90c18c3c6a3327db7808752db8d208cea0acee0abd52c52" +dependencies = [ + "path-dedot", +] + +[[package]] +name = "path-dedot" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d55e486337acb9973cdea3ec5638c1b3bcb22e573b2b7b41969e0c744d5a15e" +dependencies = [ + "once_cell", +] + [[package]] name = "pathdiff" version = "0.2.1" @@ -2156,15 +2824,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" dependencies = [ "thiserror", "ucd-trie", @@ -2172,9 +2840,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" dependencies = [ "pest", "pest_generator", @@ -2182,22 +2850,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" dependencies = [ "once_cell", "pest", @@ -2211,27 +2879,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.22", ] [[package]] @@ -2254,9 +2922,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "portable-atomic" -version = "0.3.19" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" +checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794" [[package]] name = "ppv-lite86" @@ -2299,10 +2967,16 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.56" +name = "proc-macro-hack" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] @@ -2315,9 +2989,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -2349,7 +3023,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -2393,35 +3067,57 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.2", "memchr", "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.7.1" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "remove_dir_all" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23895cfadc1917fed9c6ed76a8c2903615fa3704f7493ff82b364c6540acc02b" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "fs_at", + "lazy_static", + "libc", + "normpath", + "windows-sys 0.45.0", +] [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -2430,7 +3126,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls", + "hyper-rustls 0.24.0", "hyper-tls", "ipnet", "js-sys", @@ -2440,14 +3136,14 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.2", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util", "tower-service", "trust-dns-resolver", @@ -2457,7 +3153,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -2470,6 +3166,32 @@ dependencies = [ "quick-error", ] +[[package]] +name = "rhai" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd29fa1f740be6dc91982013957e08c3c4232d7efcfe19e12da87d50bad47758" +dependencies = [ + "ahash 0.8.3", + "bitflags", + "instant", + "num-traits", + "rhai_codegen", + "smallvec", + "smartstring", +] + +[[package]] +name = "rhai_codegen" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db74e3fdd29d969a0ec1f8e79171a6f0f71d0429293656901db382d248c4c021" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ring" version = "0.16.20" @@ -2525,9 +3247,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags", "errno", @@ -2550,10 +3272,22 @@ dependencies = [ ] [[package]] -name = "rustls-native-certs" -version = "0.6.2" +name = "rustls" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -2567,7 +3301,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", ] [[package]] @@ -2591,6 +3335,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c502bdb638f1396509467cb0580ef3b29aa2a45c5d43e5d84928241280296c" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "schannel" version = "0.1.21" @@ -2606,12 +3360,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - [[package]] name = "sct" version = "0.7.0" @@ -2624,9 +3372,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags", "core-foundation", @@ -2637,9 +3385,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -2656,9 +3404,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.162" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] @@ -2675,20 +3423,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", @@ -2703,7 +3451,16 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", ] [[package]] @@ -2741,16 +3498,28 @@ dependencies = [ ] [[package]] -name = "sha2" -version = "0.10.6" +name = "sha1_smol" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2787,6 +3556,17 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "socket2" version = "0.4.9" @@ -2797,12 +3577,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2827,9 +3623,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -2844,9 +3640,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" dependencies = [ "proc-macro2", "quote", @@ -2904,12 +3700,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.40" @@ -2927,7 +3717,17 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", ] [[package]] @@ -2943,12 +3743,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ + "itoa", + "libc", + "num_threads", "serde", "time-core", + "time-macros", ] [[package]] @@ -2957,6 +3761,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2974,9 +3796,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", @@ -2986,7 +3808,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.4.9", "tokio-macros", "windows-sys 0.48.0", ] @@ -2999,7 +3821,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] @@ -3018,11 +3840,21 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.8", "tokio", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.2", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.17.2" @@ -3058,6 +3890,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -3149,20 +4015,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.22", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -3265,10 +4131,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] -name = "unicode-ident" -version = "1.0.8" +name = "unicode-bom" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -3305,12 +4177,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", ] @@ -3320,6 +4192,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3353,7 +4231,7 @@ dependencies = [ "leb128", "log", "walrus-macro", - "wasmparser 0.77.0", + "wasmparser", ] [[package]] @@ -3370,11 +4248,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -3398,9 +4275,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3408,24 +4285,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.22", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-cli-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4780c659b883a19ddb7ced365db19f7f45cd182d832ee14de2b7ef52e88a9f" +checksum = "d21c60239a09bf9bab8dfa752be4e6c637db22296b9ded493800090448692da9" dependencies = [ "anyhow", "base64 0.9.3", @@ -3441,16 +4318,13 @@ dependencies = [ "wasm-bindgen-threads-xform", "wasm-bindgen-wasm-conventions", "wasm-bindgen-wasm-interpreter", - "wit-text", - "wit-validator", - "wit-walrus", ] [[package]] name = "wasm-bindgen-externref-xform" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d154c3843bf3b635b602ad41b56f505f8f1a25f8a0133fca4bbd0918d74efdc" +checksum = "bafbe1984f67cc12645f12ab65e6145e8ddce1ab265d0be58435f25bb0ce2608" dependencies = [ "anyhow", "walrus", @@ -3458,9 +4332,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -3470,9 +4344,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3480,22 +4354,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.22", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-multi-value-xform" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00a577fbd4be358ef8095432189b5c2e6b6e71f5081797c2032572f77d65d26" +checksum = "581419e3995571a1d2d066e360ca1c0c09da097f5a53c98e6f00d96eddaf0ffe" dependencies = [ "anyhow", "walrus", @@ -3503,15 +4377,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-bindgen-threads-xform" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa93941bae037b7b4fac4ecfc132294b828036c5990a806d0e6fd9284297d94" +checksum = "e05d272073981137e8426cf2a6830d43d1f84f988a050b2f8b210f0e266b8983" dependencies = [ "anyhow", "walrus", @@ -3520,9 +4394,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-wasm-conventions" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f5de325048d945c90600fdf66b13521f3340d85971287775c36aa99c04466b" +checksum = "0e9c65b1ff5041ea824ca24c519948aec16fb6611c617d601623c0657dfcd47b" dependencies = [ "anyhow", "walrus", @@ -3530,9 +4404,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-wasm-interpreter" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f695df44962e3a107436282232a2daa185b8453c16be8ddfb637cd2601f31128" +checksum = "7c5c796220738ab5d44666f37205728a74141c0039d1166bcf8110b26bafaa1e" dependencies = [ "anyhow", "log", @@ -3555,30 +4429,15 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.59.0" +version = "0.77.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a950e6a618f62147fd514ff445b2a0b53120d382751960797f85f058c7eda9b9" - -[[package]] -name = "wasmparser" -version = "0.77.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6" - -[[package]] -name = "wast" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1844f66a2bc8526d71690104c0e78a8e59ffa1597b7245769d174ebb91deb5" -dependencies = [ - "leb128", -] +checksum = "5fe3d5405e9ea6c1317a656d6e0820912d8b7b3607823a7596117c8f666daf6f" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -3605,9 +4464,9 @@ dependencies = [ [[package]] name = "widestring" -version = "0.5.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -3640,6 +4499,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows" version = "0.48.0" @@ -3796,6 +4670,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -3806,67 +4689,13 @@ dependencies = [ ] [[package]] -name = "wit-parser" -version = "0.2.0" +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5fd97866f4b9c8e1ed57bcf9446f3d0d8ba37e2dd01c3c612c046c053b06f7" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "anyhow", - "leb128", - "wit-schema-version", -] - -[[package]] -name = "wit-schema-version" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfee4a6a4716eefa0682e7a3b836152e894a3e4f34a9d6c2c3e1c94429bfe36a" - -[[package]] -name = "wit-text" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33358e95c77d660f1c7c07f4a93c2bd89768965e844e3c50730bb4b42658df5f" -dependencies = [ - "anyhow", - "wast", - "wit-writer", -] - -[[package]] -name = "wit-validator" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c11d93d925420e7872b226c4161849c32be38385ccab026b88df99d8ddc6ba6" -dependencies = [ - "anyhow", - "wasmparser 0.59.0", - "wit-parser", - "wit-schema-version", -] - -[[package]] -name = "wit-walrus" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad559e3e4c6404b2a6a675d44129d62a3836e3b951b90112fa1c5feb852757cd" -dependencies = [ - "anyhow", - "id-arena", - "walrus", - "wit-parser", - "wit-schema-version", - "wit-writer", -] - -[[package]] -name = "wit-writer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad01ba5e9cbcff799a0689e56a153776ea694cec777f605938cb9880d41a09" -dependencies = [ - "leb128", - "wit-schema-version", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -3878,6 +4707,12 @@ dependencies = [ "libc", ] +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" + [[package]] name = "zip" version = "0.5.13" @@ -3894,9 +4729,9 @@ dependencies = [ [[package]] name = "zip" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e92305c174683d78035cbf1b70e18db6329cc0f1b9cae0a52ca90bf5bfe7125" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "aes", "byteorder", @@ -3908,7 +4743,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.21", + "time 0.3.22", "zstd", ] diff --git a/Cargo.toml b/Cargo.toml index 7dca72934..de3644f9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT/Apache-2.0" [dependencies] # cli core -clap = { version = "3.0.14", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } thiserror = "1.0.30" wasm-bindgen-cli-support = "0.2" colored = "2.0.0" @@ -83,6 +83,8 @@ dioxus-core = { git = "https://github.com/DioxusLabs/dioxus", features = ["seria dioxus-autofmt = { git = "https://github.com/DioxusLabs/dioxus" } rsx-rosetta = { git = "https://github.com/DioxusLabs/dioxus" } open = "4.1.0" +cargo-generate = "0.18.3" +toml_edit = "0.19.11" [[bin]] path = "src/main.rs" diff --git a/src/cli/cfg.rs b/src/cli/cfg.rs index 20ed7c40e..30ca89ce9 100644 --- a/src/cli/cfg.rs +++ b/src/cli/cfg.rs @@ -4,7 +4,7 @@ use super::*; #[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsBuild { /// The index HTML file to drive the bundling process [default: index.html] - #[clap(parse(from_os_str))] + #[arg(long)] pub target: Option, /// Build in release mode [default: false] @@ -37,7 +37,7 @@ pub struct ConfigOptsBuild { #[derive(Clone, Debug, Default, Deserialize, Parser)] pub struct ConfigOptsServe { /// The index HTML file to drive the bundling process [default: index.html] - #[clap(parse(from_os_str))] + #[arg(short, long)] pub target: Option, /// Port of dev server diff --git a/src/cli/create/mod.rs b/src/cli/create/mod.rs index c448e66d1..86a5e0a3f 100644 --- a/src/cli/create/mod.rs +++ b/src/cli/create/mod.rs @@ -1,14 +1,9 @@ use super::*; -use crate::custom_error; +use cargo_generate::{GenerateArgs, TemplatePath}; -/// Build the Rust WASM app and all of its assets. #[derive(Clone, Debug, Default, Deserialize, Parser)] #[clap(name = "create")] pub struct Create { - /// Init project name - #[clap(default_value = ".")] - name: String, - /// Template path #[clap(default_value = "gh:dioxuslabs/dioxus-template", long)] template: String, @@ -16,69 +11,66 @@ pub struct Create { impl Create { pub fn create(self) -> Result<()> { - if Self::name_valid_check(self.name.clone()) { - return custom_error!("❗Unsupported project name: '{}'.", &self.name); - } + let mut args = GenerateArgs::default(); + args.template_path = TemplatePath { + auto_path: Some(self.template), + ..Default::default() + }; - let project_path = PathBuf::from(&self.name); - - if project_path.join("Dioxus.toml").is_file() || project_path.join("Cargo.toml").is_file() { - return custom_error!("🧨 Folder '{}' is initialized.", &self.name); - } - - log::info!("🔧 Start: Creating new project '{}'.", self.name); - - let output = Command::new("cargo") - .arg("generate") - .arg("--help") - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?; + let path = cargo_generate::generate(args)?; + // first run cargo fmt + let mut cmd = Command::new("cargo"); + let cmd = cmd.arg("fmt").current_dir(&path); + let output = cmd.output().expect("failed to execute process"); if !output.status.success() { - log::warn!("Tool is not installed: cargo-generate, try to install it."); - let install_output = Command::new("cargo") - .arg("install") - .arg("cargo-generate") - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output()?; - if !install_output.status.success() { - return custom_error!("Try to install cargo-generate failed."); - } + log::error!("cargo fmt failed"); + log::error!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + log::error!("stderr: {}", String::from_utf8_lossy(&output.stderr)); } - let generate_output = Command::new("cargo") - .arg("generate") - .arg(&self.template) - .arg("--name") - .arg(&self.name) - .arg("--force") - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .output()?; + // then format the toml + let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")]; + for toml_path in &toml_paths { + let toml = std::fs::read_to_string(toml_path)?; + let mut toml = toml.parse::().map_err(|e| { + anyhow::anyhow!( + "failed to parse toml at {}: {}", + toml_path.display(), + e.to_string() + ) + })?; - if !generate_output.status.success() { - return custom_error!("Generate project failed. Try to update cargo-generate."); + toml.as_table_mut().fmt(); + + let as_string = toml.to_string(); + let new_string = remove_tripple_newlines(&as_string); + let mut file = std::fs::File::create(toml_path)?; + file.write_all(new_string.as_bytes())?; } - let mut dioxus_file = File::open(project_path.join("Dioxus.toml"))?; - let mut meta_file = String::new(); - dioxus_file.read_to_string(&mut meta_file)?; - 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())?; + // remove any tripple newlines from the readme + let readme_path = path.join("README.md"); + let readme = std::fs::read_to_string(&readme_path)?; + let new_readme = remove_tripple_newlines(&readme); + let mut file = std::fs::File::create(readme_path)?; + file.write_all(new_readme.as_bytes())?; - println!(); - log::info!("💡 Project initialized:"); - log::info!("🎯> cd ./{}", self.name); - log::info!("🎯> dioxus serve"); + log::info!("Generated project at {}", path.display()); Ok(()) } +} - fn name_valid_check(name: String) -> bool { - let r = Regex::new(r"^[a-zA-Z][a-zA-Z0-9\-_]$").unwrap(); - r.is_match(&name) +fn remove_tripple_newlines(string: &str) -> String { + let mut new_string = String::new(); + for char in string.chars() { + if char == '\n' { + if new_string.ends_with("\n\n") { + continue; + } + } + new_string.push(char); } + new_string } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a27b1b794..0d9174bdf 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -17,7 +17,6 @@ use crate::{ }; use clap::{Parser, Subcommand}; use html_parser::Dom; -use regex::Regex; use serde::Deserialize; use std::{ fs::{remove_dir_all, File}, diff --git a/src/cli/translate/mod.rs b/src/cli/translate/mod.rs index 3056a6c77..67067db31 100644 --- a/src/cli/translate/mod.rs +++ b/src/cli/translate/mod.rs @@ -22,7 +22,7 @@ pub struct Translate { pub raw: Option, /// Output file, stdout if not present - #[clap(parse(from_os_str))] + #[arg(short, long)] pub output: Option, } diff --git a/src/server/mod.rs b/src/server/mod.rs index 4f19f00d2..a63a7e6b7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -150,7 +150,8 @@ pub async fn startup_hot_reload( let dist_path = config.out_dir.clone(); let (reload_tx, _) = broadcast::channel(100); - let FileMapBuildResult { map, errors } = FileMap::::create(config.crate_dir.clone()).unwrap(); + let FileMapBuildResult { map, errors } = + FileMap::::create(config.crate_dir.clone()).unwrap(); for err in errors { log::error!("{}", err); } @@ -219,8 +220,7 @@ pub async fn startup_hot_reload( let mut map = file_map.lock().unwrap(); match map.update_rsx(&path, &crate_dir) { - - Ok( UpdateResult::UpdatedRsx(msgs) )=> { + Ok(UpdateResult::UpdatedRsx(msgs)) => { messages.extend(msgs); } Ok(UpdateResult::NeedsRebuild) => { From eb631f4077288cd438b952d4c18d46eef6fb26ba Mon Sep 17 00:00:00 2001 From: Jason Schein Date: Mon, 26 Jun 2023 17:29:49 -0700 Subject: [PATCH 503/516] Update fermi badge links. (#1109) --- packages/fermi/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/fermi/README.md b/packages/fermi/README.md index f04ec5961..f276dbff5 100644 --- a/packages/fermi/README.md +++ b/packages/fermi/README.md @@ -9,22 +9,22 @@
- - + Crates.io version - - + Download - + docs.rs docs - + CI status From 90f80e4dc26ad6d7a7f536793bdbf8cf7465e03f Mon Sep 17 00:00:00 2001 From: Jon Kelley Date: Tue, 27 Jun 2023 17:40:41 -0700 Subject: [PATCH 504/516] Fix `cargo doc` by using a fixed variant of interprocess (#1129) * fix docs compilation * dont change random codeblock --- packages/hot-reload/Cargo.toml | 2 +- packages/hot-reload/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 50c4114a7..b53a87809 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -16,7 +16,7 @@ dioxus-rsx = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-html = { workspace = true, features = ["hot-reload-context"] } -interprocess = { version = "1.2.1" } +interprocess-docfix = { version = "1.2.1" } notify = "5.0.0" chrono = { version = "0.4.24", default-features = false, features = ["clock"] } serde_json = "1.0.91" diff --git a/packages/hot-reload/src/lib.rs b/packages/hot-reload/src/lib.rs index 69f132fa3..48ee50a59 100644 --- a/packages/hot-reload/src/lib.rs +++ b/packages/hot-reload/src/lib.rs @@ -10,7 +10,7 @@ use dioxus_rsx::{ hot_reload::{FileMap, FileMapBuildResult, UpdateResult}, HotReloadingContext, }; -use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; +use interprocess_docfix::local_socket::{LocalSocketListener, LocalSocketStream}; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; pub use dioxus_html::HtmlCtx; From 98fc15315220389e4c0a76903d7516be84d5331e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Wed, 28 Jun 2023 16:32:42 +0200 Subject: [PATCH 505/516] Fix: Remove play button from guide example (#1118) * fix: Remove play button from guide example * Update index.md * update other examples --- .../advanced-guides/06-subscription-api.md | 4 +- .../advanced-guides/10-concurrent-mode.md | 10 +- .../__unused/advanced-guides/11-arena-memo.md | 4 +- .../en/__unused/advanced-guides/12-signals.md | 20 ++-- .../__unused/advanced-guides/13-subtrees.md | 4 +- .../advanced-guides/custom-renderer.md | 40 ++++--- .../src/en/__unused/advanced-guides/rsx.md | 12 +- .../en/__unused/advanced-guides/testing.md | 4 +- .../src/en/__unused/attribute_spreading.md | 7 +- docs/guide/src/en/__unused/composing.md | 31 ++--- docs/guide/src/en/__unused/fanout.md | 6 +- docs/guide/src/en/__unused/index.md | 8 +- docs/guide/src/en/__unused/localstate.md | 23 ++-- docs/guide/src/en/__unused/memoization.md | 4 +- docs/guide/src/en/__unused/model_pattern.md | 11 +- docs/guide/src/en/async/spawn.md | 6 +- docs/guide/src/en/async/use_coroutine.md | 25 ++-- docs/guide/src/en/async/use_future.md | 10 +- .../src/en/best_practices/antipatterns.md | 6 +- .../src/en/best_practices/error_handling.md | 27 ++--- .../src/en/contributing/walkthrough_readme.md | 6 +- docs/guide/src/en/custom_renderer/index.md | 30 ++--- .../en/describing_ui/component_children.md | 8 +- .../src/en/describing_ui/component_props.md | 34 +++--- docs/guide/src/en/describing_ui/components.md | 6 +- docs/guide/src/en/describing_ui/index.md | 58 +++++---- .../en/describing_ui/special_attributes.md | 9 +- .../guide/src/en/fullstack/getting_started.md | 6 +- .../src/en/fullstack/server_functions.md | 2 +- docs/guide/src/en/getting_started/desktop.md | 2 +- .../src/en/getting_started/hot_reload.md | 2 +- docs/guide/src/en/getting_started/liveview.md | 12 +- docs/guide/src/en/getting_started/mobile.md | 6 +- docs/guide/src/en/getting_started/ssr.md | 6 +- docs/guide/src/en/getting_started/tui.md | 4 +- docs/guide/src/en/getting_started/web.md | 2 +- docs/guide/src/en/index.md | 7 +- .../src/en/interactivity/custom_hooks.md | 14 +-- .../src/en/interactivity/dynamic_rendering.md | 13 +-- .../src/en/interactivity/event_handlers.md | 12 +- docs/guide/src/en/interactivity/hooks.md | 20 ++-- docs/guide/src/en/interactivity/router.md | 12 +- .../src/en/interactivity/sharing_state.md | 21 ++-- docs/guide/src/en/interactivity/user_input.md | 8 +- docs/guide/src/pt-br/async/spawn.md | 6 +- docs/guide/src/pt-br/async/use_coroutine.md | 16 +-- docs/guide/src/pt-br/async/use_future.md | 6 +- .../src/pt-br/best_practices/antipatterns.md | 4 +- .../pt-br/best_practices/error_handling.md | 18 +-- docs/guide/src/pt-br/custom_renderer/index.md | 110 +++++++++--------- .../pt-br/describing_ui/component_children.md | 8 +- .../pt-br/describing_ui/component_props.md | 28 ++--- .../src/pt-br/describing_ui/components.md | 6 +- docs/guide/src/pt-br/describing_ui/index.md | 16 +-- .../pt-br/describing_ui/special_attributes.md | 4 +- .../src/pt-br/getting_started/desktop.md | 2 +- .../guide/src/pt-br/getting_started/mobile.md | 2 +- docs/guide/src/pt-br/getting_started/ssr.md | 8 +- docs/guide/src/pt-br/getting_started/tui.md | 4 +- docs/guide/src/pt-br/getting_started/web.md | 2 +- docs/guide/src/pt-br/index.md | 2 +- .../src/pt-br/interactivity/custom_hooks.md | 2 +- .../pt-br/interactivity/dynamic_rendering.md | 8 +- .../src/pt-br/interactivity/event_handlers.md | 10 +- docs/guide/src/pt-br/interactivity/hooks.md | 14 +-- docs/guide/src/pt-br/interactivity/router.md | 8 +- .../src/pt-br/interactivity/sharing_state.md | 14 +-- .../src/pt-br/interactivity/user_input.md | 4 +- docs/posts/release-0-2-0.md | 36 +++--- docs/posts/release.md | 34 +++--- .../src/guide/custom_renderer_pt-br.md | 30 ++--- docs/reference/src/guide/rsx_in_depth.md | 20 ++-- .../reference/src/guide/rsx_in_depth_pt-br.md | 20 ++-- docs/reference/src/platforms/desktop.md | 2 +- 74 files changed, 503 insertions(+), 503 deletions(-) diff --git a/docs/guide/src/en/__unused/advanced-guides/06-subscription-api.md b/docs/guide/src/en/__unused/advanced-guides/06-subscription-api.md index 19ec66563..1d53a8d61 100644 --- a/docs/guide/src/en/__unused/advanced-guides/06-subscription-api.md +++ b/docs/guide/src/en/__unused/advanced-guides/06-subscription-api.md @@ -2,7 +2,7 @@ Yew subscriptions are used to schedule update for components into the future. The `Context` object can create subscriptions: -```rust +```rust, no_run fn Component(cx: Component) -> DomTree { let update = cx.schedule(); @@ -19,7 +19,7 @@ The subscription API exposes this functionality allowing hooks and state managem some state or event occurs outside of the component. For instance, the `use_context` hook uses this to subscribe components that use a particular context. -```rust +```rust, no_run fn use_context(cx: Scope) -> I { } diff --git a/docs/guide/src/en/__unused/advanced-guides/10-concurrent-mode.md b/docs/guide/src/en/__unused/advanced-guides/10-concurrent-mode.md index 6bbebbeed..b92897157 100644 --- a/docs/guide/src/en/__unused/advanced-guides/10-concurrent-mode.md +++ b/docs/guide/src/en/__unused/advanced-guides/10-concurrent-mode.md @@ -4,7 +4,7 @@ Concurrent mode provides a mechanism for building efficient asynchronous compone To make a component asynchronous, simply change its function signature to async. -```rust +```rust, no_run fn Example(cx: Scope) -> Vnode { rsx!{
"Hello world!"
} } @@ -12,7 +12,7 @@ fn Example(cx: Scope) -> Vnode { becomes -```rust +```rust, no_run async fn Example(cx: Scope) -> Vnode { rsx!{
"Hello world!"
} } @@ -20,7 +20,7 @@ async fn Example(cx: Scope) -> Vnode { Now, logic in components can be awaited to delay updates of the component and its children. Like so: -```rust +```rust, no_run async fn Example(cx: Scope) -> Vnode { let name = fetch_name().await; rsx!{
"Hello {name}"
} @@ -39,7 +39,7 @@ Instead, we suggest using hooks and future combinators that can safely utilize t As part of our Dioxus hooks crate, we provide a data loader hook which pauses a component until its async dependencies are ready. This caches requests, reruns the fetch if dependencies have changed, and provides the option to render something else while the component is loading. -```rust +```rust, no_run async fn ExampleLoader(cx: Scope) -> Vnode { /* Fetch, pause the component from rendering at all. @@ -61,7 +61,7 @@ async fn ExampleLoader(cx: Scope) -> Vnode { } ``` -```rust +```rust, no_run async fn Example(cx: Scope) -> DomTree { // Diff this set between the last set // Check if we have any outstanding tasks? diff --git a/docs/guide/src/en/__unused/advanced-guides/11-arena-memo.md b/docs/guide/src/en/__unused/advanced-guides/11-arena-memo.md index ca44a6b7e..49fedda11 100644 --- a/docs/guide/src/en/__unused/advanced-guides/11-arena-memo.md +++ b/docs/guide/src/en/__unused/advanced-guides/11-arena-memo.md @@ -10,7 +10,7 @@ https://dmitripavlutin.com/use-react-memo-wisely/ This behavior is defined as an attribute implicit to user components. When in React land you might wrap a component with `react.memo`, Dioxus components are automatically memoized via an implicit attribute. You can manually configure this behavior on any component with "nomemo" to disable memoization. -```rust +```rust, no_run fn test() -> DomTree { html! { <> @@ -42,7 +42,7 @@ fn test_component(cx: Scope, name: String) -> Element { Take a component like this: -```rust +```rust, no_run fn test(cx: Scope) -> DomTree { let Bundle { alpha, beta, gamma } = use_context::(cx); html! { diff --git a/docs/guide/src/en/__unused/advanced-guides/12-signals.md b/docs/guide/src/en/__unused/advanced-guides/12-signals.md index c7012a119..df13b91d8 100644 --- a/docs/guide/src/en/__unused/advanced-guides/12-signals.md +++ b/docs/guide/src/en/__unused/advanced-guides/12-signals.md @@ -10,7 +10,7 @@ By default, Dioxus will only try to diff subtrees of components with dynamic con Your component today might look something like this: -```rust +```rust, no_run fn Comp(cx: Scope) -> DomTree { let (title, set_title) = use_state(cx, || "Title".to_string()); cx.render(rsx!{ @@ -24,7 +24,7 @@ fn Comp(cx: Scope) -> DomTree { This component is fairly straightforward – the input updates its own value on every change. However, every call to set_title will re-render the component. If we add a large list, then every time we update the title input, Dioxus will need to diff the entire list, over, and over, and over. This is **a lot** of wasted clock-cycles! -```rust +```rust, no_run fn Comp(cx: Scope) -> DomTree { let (title, set_title) = use_state(cx, || "Title".to_string()); cx.render(rsx!{ @@ -47,7 +47,7 @@ Many experienced React developers will just say "this is bad design" – but we We can use signals to generate a two-way binding between data and the input box. Our text input is now just a two-line component! -```rust +```rust, no_run fn Comp(cx: Scope) -> DomTree { let mut title = use_signal(cx, || String::from("Title")); cx.render(rsx!(input { value: title })) @@ -56,7 +56,7 @@ fn Comp(cx: Scope) -> DomTree { For a slightly more interesting example, this component calculates the sum between two numbers, but totally skips the diffing process. -```rust +```rust, no_run fn Calculator(cx: Scope) -> DomTree { let mut a = use_signal(cx, || 0); let mut b = use_signal(cx, || 0); @@ -71,7 +71,7 @@ fn Calculator(cx: Scope) -> DomTree { Do you notice how we can use built-in operations on signals? Under the hood, we actually create a new derived signal that depends on `a` and `b`. Whenever `a` or `b` update, then `c` will update. If we need to create a new derived signal that's more complex than a basic operation (`std::ops`) we can either chain signals together or combine them: -```rust +```rust, no_run let mut a = use_signal(cx, || 0); let mut b = use_signal(cx, || 0); @@ -83,7 +83,7 @@ let c = a.with(b).map(|(a, b)| *a + *b); If we ever need to get the value out of a signal, we can simply `deref` it. -```rust +```rust, no_run let mut a = use_signal(cx, || 0); let c = *a + *b; ``` @@ -94,7 +94,7 @@ Calling `deref` or `deref_mut` is actually more complex than it seems. When a va Sometimes you want a signal to propagate across your app, either through far-away siblings or through deeply-nested components. In these cases, we use Dirac: Dioxus's first-class state management toolkit. Dirac atoms automatically implement the Signal API. This component will bind the input element to the `TITLE` atom. -```rust +```rust, no_run const TITLE: Atom = || "".to_string(); const Provider: Component = |cx|{ let title = use_signal(cx, &TITLE); @@ -104,7 +104,7 @@ const Provider: Component = |cx|{ If we use the `TITLE` atom in another component, we can cause updates to flow between components without calling render or diffing either component trees: -```rust +```rust, no_run const Receiver: Component = |cx|{ let title = use_signal(cx, &TITLE); log::info!("This will only be called once!"); @@ -130,7 +130,7 @@ By default, Dioxus is limited when you use iter/map. With the `For` component, y Dioxus automatically understands how to use your signals when mixed with iterators through `Deref`/`DerefMut`. This lets you efficiently map collections while avoiding the re-rendering of lists. In essence, signals act as a hint to Dioxus on how to avoid un-necessary checks and renders, making your app faster. -```rust +```rust, no_run const DICT: AtomFamily = |_| {}; const List: Component = |cx|{ let dict = use_signal(cx, &DICT); @@ -146,7 +146,7 @@ const List: Component = |cx|{ Apps that use signals will enjoy a pleasant hybrid of server-side and client-side rendering. -```rust +```rust, no_run ``` diff --git a/docs/guide/src/en/__unused/advanced-guides/13-subtrees.md b/docs/guide/src/en/__unused/advanced-guides/13-subtrees.md index 0108186eb..d7a8ba1d7 100644 --- a/docs/guide/src/en/__unused/advanced-guides/13-subtrees.md +++ b/docs/guide/src/en/__unused/advanced-guides/13-subtrees.md @@ -22,13 +22,11 @@ The desktop renderer comes pre-loaded with the window and notification subtree p Subtrees also solve the "bridging" issues in React where two different renderers need two different VirtualDoms to work properly. In Dioxus, you only ever need one VirtualDom and the right renderer plugins. - ## API Due to their importance in the hierarchy, Components – not nodes – are treated as subtree roots. - -```rust +```rust, no_run fn Subtree

(cx: Scope

) -> DomTree { diff --git a/docs/guide/src/en/__unused/advanced-guides/custom-renderer.md b/docs/guide/src/en/__unused/advanced-guides/custom-renderer.md index 194fff54a..b14243dae 100644 --- a/docs/guide/src/en/__unused/advanced-guides/custom-renderer.md +++ b/docs/guide/src/en/__unused/advanced-guides/custom-renderer.md @@ -22,7 +22,7 @@ For reference, check out the WebSys renderer as a starting point for your custom The current `RealDom` trait lives in `dioxus-core/diff`. A version of it is provided here (but might not be up-to-date): -```rust +```rust, no_run pub trait RealDom<'a> { fn handle_edit(&mut self, edit: DomEdit); fn request_available_node(&mut self) -> ElementId; @@ -32,7 +32,7 @@ pub trait RealDom<'a> { For reference, the "DomEdit" type is a serialized enum that represents an atomic operation occurring on the RealDom. The variants roughly follow this set: -```rust +```rust, no_run enum DomEdit { PushRoot, AppendChildren, @@ -51,12 +51,11 @@ enum DomEdit { The Dioxus diffing mechanism operates as a [stack machine](https://en.wikipedia.org/wiki/Stack_machine) where the "push_root" method pushes a new "real" DOM node onto the stack and "append_child" and "replace_with" both remove nodes from the stack. - ### An example For the sake of understanding, lets consider this example – a very simple UI declaration: -```rust +```rust, no_run rsx!( h1 {"hello world"} ) ``` @@ -64,7 +63,7 @@ To get things started, Dioxus must first navigate to the container of this h1 ta When the renderer receives this instruction, it pushes the actual Node onto its own stack. The real renderer's stack will look like this: -```rust +```rust, no_run instructions: [ PushRoot(Container) ] @@ -75,7 +74,7 @@ stack: [ Next, Dioxus will encounter the h1 node. The diff algorithm decides that this node needs to be created, so Dioxus will generate the DomEdit `CreateElement`. When the renderer receives this instruction, it will create an unmounted node and push into its own stack: -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -85,8 +84,10 @@ stack: [ h1, ] ``` + Next, Dioxus sees the text node, and generates the `CreateTextNode` DomEdit: -```rust + +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -98,9 +99,10 @@ stack: [ "hello world" ] ``` + Remember, the text node is not attached to anything (it is unmounted) so Dioxus needs to generate an Edit that connects the text node to the h1 element. It depends on the situation, but in this case we use `AppendChildren`. This pops the text node off the stack, leaving the h1 element as the next element in line. -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -112,8 +114,10 @@ stack: [ h1 ] ``` + We call `AppendChildren` again, popping off the h1 node and attaching it to the parent: -```rust + +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -125,8 +129,10 @@ stack: [ ContainerNode, ] ``` + Finally, the container is popped since we don't need it anymore. -```rust + +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -137,8 +143,10 @@ instructions: [ ] stack: [] ``` + Over time, our stack looked like this: -```rust + +```rust, no_run [] [Container] [Container, h1] @@ -164,7 +172,7 @@ Like most GUIs, Dioxus relies on an event loop to progress the VirtualDOM. The V The code for the WebSys implementation is straightforward, so we'll add it here to demonstrate how simple an event loop is: -```rust +```rust, no_run pub async fn run(&mut self) -> dioxus_core::error::Result<()> { // Push the body element onto the WebsysDom's stack machine let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom()); @@ -194,7 +202,7 @@ pub async fn run(&mut self) -> dioxus_core::error::Result<()> { It's important that you decode the real events from your event system into Dioxus' synthetic event system (synthetic meaning abstracted). This simply means matching your event type and creating a Dioxus `VirtualEvent` type. Your custom event must implement the corresponding event trait. Right now, the VirtualEvent system is modeled almost entirely around the HTML spec, but we are interested in slimming it down. -```rust +```rust, no_run fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { match event.type_().as_str() { "keydown" | "keypress" | "keyup" => { @@ -224,7 +232,7 @@ These custom elements are defined as unit structs with trait implementations. For example, the `div` element is (approximately!) defined as such: -```rust +```rust, no_run struct div; impl div { /// Some glorious documentation about the class property. @@ -235,8 +243,8 @@ impl div { // more attributes } ``` -You've probably noticed that many elements in the `rsx!` and `html!` macros support on-hover documentation. The approach we take to custom elements means that the unit struct is created immediately where the element is used in the macro. When the macro is expanded, the doc comments still apply to the unit struct, giving tons of in-editor feedback, even inside a proc macro. +You've probably noticed that many elements in the `rsx!` and `html!` macros support on-hover documentation. The approach we take to custom elements means that the unit struct is created immediately where the element is used in the macro. When the macro is expanded, the doc comments still apply to the unit struct, giving tons of in-editor feedback, even inside a proc macro. ## Compatibility @@ -252,7 +260,7 @@ The best hooks will properly detect the target platform and still provide functi This particular code _will panic_ due to the unwrap on downcast_ref. Try to avoid these types of patterns. -```rust +```rust, no_run let div_ref = use_node_ref(cx); cx.render(rsx!{ diff --git a/docs/guide/src/en/__unused/advanced-guides/rsx.md b/docs/guide/src/en/__unused/advanced-guides/rsx.md index 062246490..169a57875 100644 --- a/docs/guide/src/en/__unused/advanced-guides/rsx.md +++ b/docs/guide/src/en/__unused/advanced-guides/rsx.md @@ -5,11 +5,14 @@ Many modern frameworks provide a domain-specific-language for declaring user-int With Dioxus, we actually ship two different macros – a macro that mimics JSX (the `html!` macro) and a macro that mimics Rust's native nested-struct syntax (the `rsx!` macro). These macros simply transform their inputs into NodeFactory calls. For instance, this html! call: -```rust + +```rust, no_run html!(

"hello world"
) ``` + becomes this NodeFactory call: -```rust + +```rust, no_run |f| f.element( dioxus_elements::div, // tag [], // listeners @@ -18,6 +21,7 @@ becomes this NodeFactory call: None // key ) ``` + The NodeFactory API is fairly ergonomic, making it a viable option to use directly. The NodeFactory API is also compile-time correct and has incredible syntax highlighting support. We use what Rust calls a "unit type" – the `dioxus_elements::div` and associated methods to ensure that a `div` can only have attributes associated with `div`s. This lets us tack on relevant documentation, autocomplete support, and jump-to-definition for methods and attributes. ![Compile time correct syntax](../images/compiletimecorrect.png) @@ -28,7 +32,7 @@ The html! macro supports a limited subset of the html standard. Rust's macro par However, writing HTML by hand is a bit tedious – IDE tools for Rust don't support linting/autocomplete/syntax highlighting. We suggest using RSX – it's more natural for Rust programs and _does_ integrate well with Rust IDE tools. -```rust +```rust, no_run let name = "jane"; let pending = false; let count = 10; @@ -50,7 +54,7 @@ When helpful, the Dioxus VSCode extension provides a way of converting a selecti It's also a bit easier on the eyes than HTML. -```rust +```rust, no_run dioxus::ssr::render_lazy(rsx! { div { p {"Hello, {name}!"} diff --git a/docs/guide/src/en/__unused/advanced-guides/testing.md b/docs/guide/src/en/__unused/advanced-guides/testing.md index 2f0f97b67..87e72309c 100644 --- a/docs/guide/src/en/__unused/advanced-guides/testing.md +++ b/docs/guide/src/en/__unused/advanced-guides/testing.md @@ -2,7 +2,7 @@ To test your Rust code, you can annotate any function with the `#[test]` block. In VSCode with RA, this will provide a lens to click and run the test. -```rust +```rust, no_run #[test] fn component_runs() { assert!(true) @@ -11,7 +11,7 @@ fn component_runs() { This will test your Rust code _without_ going through the browser. This is ideal for squashing logic bugs and ensuring components render appropriately when the browsers's DOM is not needed. If you need to run tests in the browser, you can annotate your blocks with the `#[dioxus::test]` block. -```rust +```rust, no_run #[dioxus::test] fn runs_in_browser() { // ... diff --git a/docs/guide/src/en/__unused/attribute_spreading.md b/docs/guide/src/en/__unused/attribute_spreading.md index fcf799f3d..9bbb30698 100644 --- a/docs/guide/src/en/__unused/attribute_spreading.md +++ b/docs/guide/src/en/__unused/attribute_spreading.md @@ -1,6 +1,6 @@ In the cases where you need to pass arbitrary element properties into a component – say to add more functionality to the `` tag, Dioxus will accept any quoted fields. This is similar to adding arbitrary fields to regular elements using quotes. -```rust +```rust, no_run rsx!( Clickable { @@ -13,7 +13,7 @@ rsx!( For a component to accept these attributes, you must add an `attributes` field to your component's properties. We can use the spread syntax to add these attributes to whatever nodes are in our component. -```rust +```rust, no_run #[derive(Props)] struct ClickableProps<'a> { attributes: Attributes<'a> @@ -26,4 +26,5 @@ fn clickable(cx: Scope>) -> Element { "Any link, anywhere" } )) -} \ No newline at end of file +} +``` diff --git a/docs/guide/src/en/__unused/composing.md b/docs/guide/src/en/__unused/composing.md index 06648a4c1..82435d6f3 100644 --- a/docs/guide/src/en/__unused/composing.md +++ b/docs/guide/src/en/__unused/composing.md @@ -1,6 +1,6 @@ # Thinking in Reactively -We've finally reached the point in our tutorial where we can talk about the theory of Reactivity. We've talked about defining a declarative view, but not about the aspects that make our code *reactive*. +We've finally reached the point in our tutorial where we can talk about the theory of Reactivity. We've talked about defining a declarative view, but not about the aspects that make our code _reactive_. Understanding the theory of reactive programming is essential to making sense of Dioxus and writing effective, performant UIs. @@ -15,7 +15,7 @@ This section is a bit long, but worth the read. We recommend coffee, tea, and/or ## Reactive Programming -Dioxus is one of a handful of Rust libraries that provide a "Reactive Programming Model". The term "Reactive programming" is a classification of programming paradigm – much like functional or imperative programming. This is a very important distinction since it affects how we *think* about our code. +Dioxus is one of a handful of Rust libraries that provide a "Reactive Programming Model". The term "Reactive programming" is a classification of programming paradigm – much like functional or imperative programming. This is a very important distinction since it affects how we _think_ about our code. Reactive programming is a programming model concerned with deriving computations from asynchronous data flow. Most reactive programs are comprised of datasources, intermediate computations, and a final result. @@ -33,7 +33,7 @@ In Reactive Programming, we don't think about whether or not we should reevaluat In Rust, our reactive app would look something like: -```rust +```rust, no_run fn compute_g(t: i32, seconds: i32) -> bool { t > seconds } @@ -55,7 +55,7 @@ The Dioxus VirtualDom provides us a framework for reactive programming. When we If we represented the reactive graph presented above in Dioxus, it would look very similar: -```rust +```rust, no_run // Declare a component that holds our datasources and calculates `g` fn RenderGraph(cx: Scope) -> Element { let seconds = use_datasource(SECONDS); @@ -83,11 +83,11 @@ fn RenderT(cx: Scope, seconds: i32, constant: i32) -> Element { With this app, we've defined three components. Our top-level component provides our datasources (the hooks), computation nodes (child components), and a final value (what's "rendered"). -Now, whenever the `constant` changes, our `RenderT` component will be re-rendered. However, if `seconds` doesn't change, then we don't need to re-render `RenderG` because the input is the same. If `seconds` *does* change, then both RenderG and RenderT will be reevaluated. +Now, whenever the `constant` changes, our `RenderT` component will be re-rendered. However, if `seconds` doesn't change, then we don't need to re-render `RenderG` because the input is the same. If `seconds` _does_ change, then both RenderG and RenderT will be reevaluated. Dioxus is "Reactive" because it provides this framework for us. All we need to do is write our own tiny units of computation and Dioxus figures out which components need to be reevaluated automatically. -These extra checks and algorithms add some overhead, which is why you see projects like [Sycamore](http://sycamore-rs.netlify.app) and [SolidJS](http://solidjs.com) eliminating them altogether. Dioxus is *really* fast, so we're willing to exchange the added overhead for improved developer experience. +These extra checks and algorithms add some overhead, which is why you see projects like [Sycamore](http://sycamore-rs.netlify.app) and [SolidJS](http://solidjs.com) eliminating them altogether. Dioxus is _really_ fast, so we're willing to exchange the added overhead for improved developer experience. ## How do we update values in our dataflow graph? @@ -104,7 +104,7 @@ Technically, the root props of the VirtualDom are a third datasource, but since For local state in hooks, Dioxus gives us the `use_hook` method which returns an `&mut T` without any requirements. This means raw hook values are not tracked by Dioxus. In fact, we could write a component that modifies a hook value directly: -```rust +```rust, no_run fn app(cx: Scope) -> Element { let mut count = cx.use_hook(|_| 0); cx.render(rsx!{ @@ -118,7 +118,7 @@ fn app(cx: Scope) -> Element { However, when this value is written to, the component does not know to be reevaluated. We must explicitly tell Dioxus that this component is "dirty" and needs to be re-rendered. This is done through the `cx.needs_update` method: -```rust +```rust, no_run button { onclick: move |_| { *count += 1; @@ -130,7 +130,7 @@ button { Now, whenever we click the button, the value will change and the component will be re-rendered. -> Re-rendering is when Dioxus calls your function component *again*. Component functions will be called over and over throughout their lifetime, so they should be mostly side-effect free. +> Re-rendering is when Dioxus calls your function component _again_. Component functions will be called over and over throughout their lifetime, so they should be mostly side-effect free. ### Understand this! @@ -146,9 +146,9 @@ To make app-global state easier to reason about, Dioxus makes all values provide In these cases, App-Global state needs to manually track which components need to be re-generated. -To regenerate *any* component in your app, you can get a handle to the Dioxus' internal scheduler through `schedule_update_any`: +To regenerate _any_ component in your app, you can get a handle to the Dioxus' internal scheduler through `schedule_update_any`: -```rust +```rust, no_run let force_render = cx.schedule_update_any(); // force a render of the root component @@ -179,9 +179,9 @@ From here, Dioxus computes the difference between these trees and updates the Re ## Suppressing Renders -So, we know how to make Dioxus render, but how do we *stop* it? What if we *know* that our state didn't change and we shouldn't render and diff new nodes because they'll be exactly the same as the last time? +So, we know how to make Dioxus render, but how do we _stop_ it? What if we _know_ that our state didn't change and we shouldn't render and diff new nodes because they'll be exactly the same as the last time? -In these cases, you want to reach for *memoization*. In Dioxus, memoization involves preventing a component from rendering again if its props didn't change since the last time it attempted to render. +In these cases, you want to reach for _memoization_. In Dioxus, memoization involves preventing a component from rendering again if its props didn't change since the last time it attempted to render. Visually, you can tell that a component will only re-render if the new value is sufficiently different than the old one. @@ -196,7 +196,7 @@ Visually, you can tell that a component will only re-render if the new value is This is why when you `derive(Props)`, you must also implement the `PartialEq` trait. To override the memoization strategy for a component, you can simply implement your own PartialEq. -```rust +```rust, no_run struct CustomProps { val: i32, } @@ -215,7 +215,7 @@ However, for components that borrow data, it doesn't make sense to implement Par You can technically override this behavior by implementing the `Props` trait manually, though it's unsafe and easy to mess up: -```rust +```rust, no_run unsafe impl Properties for CustomProps { fn memoize(&self, other &Self) -> bool { self != other @@ -224,6 +224,7 @@ unsafe impl Properties for CustomProps { ``` TLDR: + - Dioxus checks if props changed between renders - If props changed according to PartialEq, Dioxus re-renders the component - Props that have a lifetime (ie `<'a>`) will always be re-rendered diff --git a/docs/guide/src/en/__unused/fanout.md b/docs/guide/src/en/__unused/fanout.md index 20c4c9974..63b7d3403 100644 --- a/docs/guide/src/en/__unused/fanout.md +++ b/docs/guide/src/en/__unused/fanout.md @@ -6,7 +6,7 @@ One of the most reliable state management patterns in large Dioxus apps is `fan- With `fan-out`, our individual components at "leaf" position of our VirtualDom are "pure", making them easily reusable, testable, and deterministic. For instance, the "title" bar of our app might be a fairly complicated component. -```rust +```rust, no_run #[derive(Props, PartialEq)] struct TitlebarProps { title: String, @@ -26,7 +26,7 @@ fn Titlebar(cx: Scope) -> Element { If we used global state like use_context or fermi, we might be tempted to inject our `use_read` directly into the component. -```rust +```rust, no_run fn Titlebar(cx: Scope) -> Element { let title = use_read(cx, TITLE); let subtitle = use_read(cx, SUBTITLE); @@ -41,7 +41,7 @@ For many apps – this is a fine pattern, especially if the component is a one-o To enable our titlebar component to be used across apps, we want to lift our atoms upwards and out of the Titlebar component. We would organize a bunch of other components in this section of the app to share some of the same state. -```rust +```rust, no_run fn DocsiteTitlesection(cx: Scope) { let title = use_read(cx, TITLE); let subtitle = use_read(cx, SUBTITLE); diff --git a/docs/guide/src/en/__unused/index.md b/docs/guide/src/en/__unused/index.md index 1732afdae..d1863a465 100644 --- a/docs/guide/src/en/__unused/index.md +++ b/docs/guide/src/en/__unused/index.md @@ -4,16 +4,15 @@ Every app you'll build with Dioxus will have some sort of state that needs to be In this chapter, we'll cover the various ways to manage state, the appropriate terminology, various patterns, and some problems you might run into. - ## The Problem Why do people say state management is so difficult? What does it mean? -Generally, state management is the code you need to write to ensure that your app renders the *correct* content. If the user inputs a name, then you need to display the appropriate response – like alerts, validation, and disable/enable various elements on the page. Things can quickly become tricky if you need loading screens and cancellable tasks. +Generally, state management is the code you need to write to ensure that your app renders the _correct_ content. If the user inputs a name, then you need to display the appropriate response – like alerts, validation, and disable/enable various elements on the page. Things can quickly become tricky if you need loading screens and cancellable tasks. -For the simplest of apps, all of your state can enter the app from the root props. This is common in server-side rendering – we can collect all of the required state *before* rendering the content. +For the simplest of apps, all of your state can enter the app from the root props. This is common in server-side rendering – we can collect all of the required state _before_ rendering the content. -```rust +```rust, no_run let all_content = get_all_content().await; let output = dioxus::ssr::render_lazy(rsx!{ @@ -27,7 +26,6 @@ With this incredibly simple setup, it is highly unlikely that you'll have render However, most of your apps will store state inside of the Dioxus VirtualDom – either through local state or global state. - ## Your options To deal with complexity, you have a couple of options: diff --git a/docs/guide/src/en/__unused/localstate.md b/docs/guide/src/en/__unused/localstate.md index 2a20d77d4..fbc04272a 100644 --- a/docs/guide/src/en/__unused/localstate.md +++ b/docs/guide/src/en/__unused/localstate.md @@ -6,7 +6,7 @@ The first step to dealing with complexity in your app is to refactor your state Let's say we're managing the state for a list of Todos. Whenever we edit the todo, we want the list to update. We might've started building our app but putting everything into a single `use_ref`. -```rust +```rust, no_run struct Todo { contents: String, is_hovered: bool, @@ -29,7 +29,7 @@ cx.render(rsx!{ As shown above, whenever the todo is hovered, we want to set its state: -```rust +```rust, no_run todos.write()[0].is_hovered = true; ``` @@ -37,7 +37,7 @@ As the amount of interactions goes up, so does the complexity of our state. Shou Instead, let's refactor our Todo component to handle its own state: -```rust +```rust, no_run #[inline_props] fn Todo<'a>(cx: Scope, todo: &'a Todo) -> Element { let is_hovered = use_state(cx, || false); @@ -53,16 +53,15 @@ fn Todo<'a>(cx: Scope, todo: &'a Todo) -> Element { Now, we can simplify our Todo data model to get rid of local UI state: -```rust +```rust, no_run struct Todo { contents: String, } ``` -This is not only better for encapsulation and abstraction, but it's only more performant! Whenever a Todo is hovered, we won't need to re-render *every* Todo again – only the Todo that's currently being hovered. +This is not only better for encapsulation and abstraction, but it's only more performant! Whenever a Todo is hovered, we won't need to re-render _every_ Todo again – only the Todo that's currently being hovered. - -Wherever possible, you should try to refactor the "view" layer *out* of your data model. +Wherever possible, you should try to refactor the "view" layer _out_ of your data model. ## Immutability @@ -72,7 +71,7 @@ In these scenarios consider breaking your `use_ref` into individual `use_state`s You might've started modeling your component with a struct and use_ref -```rust +```rust, no_run struct State { count: i32, color: &'static str, @@ -85,17 +84,17 @@ let state = use_ref(cx, State::new) The "better" approach for this particular component would be to break the state apart into different values: -```rust +```rust, no_run let count = use_state(cx, || 0); let color = use_state(cx, || "red"); let names = use_state(cx, HashMap::new); ``` -You might recognize that our "names" value is a HashMap – which is not terribly cheap to clone every time we update its value. To solve this issue, we *highly* suggest using a library like [`im`](https://crates.io/crates/im) which will take advantage of structural sharing to make clones and mutations much cheaper. +You might recognize that our "names" value is a HashMap – which is not terribly cheap to clone every time we update its value. To solve this issue, we _highly_ suggest using a library like [`im`](https://crates.io/crates/im) which will take advantage of structural sharing to make clones and mutations much cheaper. When combined with the `make_mut` method on `use_state`, you can get really succinct updates to collections: -```rust +```rust, no_run let todos = use_state(cx, im_rc::HashMap::default); todos.make_mut().insert("new todo", Todo { @@ -106,5 +105,3 @@ todos.make_mut().insert("new todo", Todo { ## Moving on This particular local patterns are powerful but is not a cure-all for state management problems. Sometimes your state problems are much larger than just staying local to a component. - - diff --git a/docs/guide/src/en/__unused/memoization.md b/docs/guide/src/en/__unused/memoization.md index 9bd0e8b24..4baa73be9 100644 --- a/docs/guide/src/en/__unused/memoization.md +++ b/docs/guide/src/en/__unused/memoization.md @@ -1,11 +1,10 @@ - ## Memoization Dioxus uses Memoization for a more efficient user interface. Memoization is the process in which we check if a component actually needs to be re-rendered when its props change. If a component's properties change but they wouldn't affect the output, then we don't need to re-render the component, saving time! For example, let's say we have a component that has two children: -```rust +```rust, no_run fn Demo(cx: Scope) -> Element { // don't worry about these 2, we'll cover them later let name = use_state(cx, || String::from("bob")); @@ -25,4 +24,3 @@ Dioxus memoizes owned components. It uses `PartialEq` to determine if a componen > This means you can always rely on props with `PartialEq` or no props at all to act as barriers in your app. This can be extremely useful when building larger apps where properties frequently change. By moving our state into a global state management solution, we can achieve precise, surgical re-renders, improving the performance of our app. Borrowed Props cannot be safely memoized. However, this is not a problem – Dioxus relies on the memoization of their parents to determine if they need to be rerendered. - diff --git a/docs/guide/src/en/__unused/model_pattern.md b/docs/guide/src/en/__unused/model_pattern.md index e66b6981c..c9bf9f6b5 100644 --- a/docs/guide/src/en/__unused/model_pattern.md +++ b/docs/guide/src/en/__unused/model_pattern.md @@ -1,12 +1,10 @@ - - ## Using UseRef with "models" One option for state management that UseRef enables is the development of a "model" for your components. This particular pattern enables you to model your state with regular structs. For instance, our calculator example uses a struct to model the state. -```rust +```rust, no_run struct Calculator { display_value: String, @@ -18,7 +16,7 @@ struct Calculator { Our component is really simple – we just call `use_ref` to get an initial calculator state. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let state = use_ref(cx, Calculator::new); @@ -30,7 +28,7 @@ fn app(cx: Scope) -> Element { In our UI, we can then use `read` and a helper method to get data out of the model. -```rust +```rust, no_run // Our accessor method impl Calculator { fn formatted_display(&self) -> String { @@ -49,7 +47,7 @@ cx.render(rsx!{ To modify the state, we can setup a helper method and then attach it to a callback. -```rust +```rust, no_run // our helper impl Calculator { fn clear_display(&mut self) { @@ -64,4 +62,3 @@ cx.render(rsx!{ } }) ``` - diff --git a/docs/guide/src/en/async/spawn.md b/docs/guide/src/en/async/spawn.md index b29f63ebf..2f86ad159 100644 --- a/docs/guide/src/en/async/spawn.md +++ b/docs/guide/src/en/async/spawn.md @@ -2,11 +2,11 @@ The `use_future` and `use_coroutine` hooks are useful if you want to unconditionally spawn the future. Sometimes, though, you'll want to only spawn a future in response to an event, such as a mouse click. For example, suppose you need to send a request when the user clicks a "log in" button. For this, you can use `cx.spawn`: -```rust +```rust, no_run {{#include ../../../examples/spawn.rs:spawn}} ``` -> Note: `spawn` will always spawn a *new* future. You most likely don't want to call it on every render. +> Note: `spawn` will always spawn a _new_ future. You most likely don't want to call it on every render. Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the future. @@ -14,6 +14,6 @@ Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the Sometimes, you might want to spawn a background task that needs multiple threads or talk to hardware that might block your app code. In these cases, we can directly spawn a Tokio task from our future. For Dioxus-Desktop, your task will be spawned onto Tokio's Multithreaded runtime: -```rust +```rust, no_run {{#include ../../../examples/spawn.rs:tokio}} ``` diff --git a/docs/guide/src/en/async/use_coroutine.md b/docs/guide/src/en/async/use_coroutine.md index 537e27ea4..8d4b8f99a 100644 --- a/docs/guide/src/en/async/use_coroutine.md +++ b/docs/guide/src/en/async/use_coroutine.md @@ -8,7 +8,7 @@ Like regular futures, code in a coroutine will run until the next `await` point The `use_coroutine` hook allows you to create a coroutine. Most coroutines we write will be polling loops using async/await. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let ws: &UseCoroutine<()> = use_coroutine(cx, |rx| async move { // Connect to some sort of service @@ -26,7 +26,7 @@ For many services, a simple async loop will handle the majority of use cases. However, if we want to temporarily disable the coroutine, we can "pause" it using the `pause` method, and "resume" it using the `resume` method: -```rust +```rust, no_run let sync: &UseCoroutine<()> = use_coroutine(cx, |rx| async move { // code for syncing }); @@ -58,7 +58,7 @@ The future must be `'static` – so any values captured by the task cannot carry You can use [to_owned](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) to create a clone of the hook handle which can be moved into the async closure. -```rust +```rust, no_run let sync_status = use_state(cx, || Status::Launching); let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| { let sync_status = sync_status.to_owned(); @@ -73,7 +73,7 @@ let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| { To make this a bit less verbose, Dioxus exports the `to_owned!` macro which will create a binding as shown above, which can be quite helpful when dealing with many values. -```rust +```rust, no_run let sync_status = use_state(cx, || Status::Launching); let load_status = use_state(cx, || Status::Launching); let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| { @@ -88,10 +88,9 @@ let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| { You might've noticed the `use_coroutine` closure takes an argument called `rx`. What is that? Well, a common pattern in complex apps is to handle a bunch of async code at once. With libraries like Redux Toolkit, managing multiple promises at once can be challenging and a common source of bugs. -With Coroutines, we can centralize our async logic. The `rx` parameter is an Channel that allows code external to the coroutine to send data *into* the coroutine. Instead of looping on an external service, we can loop on the channel itself, processing messages from within our app without needing to spawn a new future. To send data into the coroutine, we would call "send" on the handle. +With Coroutines, we can centralize our async logic. The `rx` parameter is an Channel that allows code external to the coroutine to send data _into_ the coroutine. Instead of looping on an external service, we can loop on the channel itself, processing messages from within our app without needing to spawn a new future. To send data into the coroutine, we would call "send" on the handle. - -```rust +```rust, no_run use futures_util::stream::StreamExt; enum ProfileUpdate { @@ -119,13 +118,11 @@ cx.render(rsx!{ }) ``` - > Note: In order to use/run the `rx.next().await` statement you will need to extend the [`Stream`] trait (used by [`UnboundedReceiver`]) by adding 'futures_util' as a dependency to your project and adding the `use futures_util::stream::StreamExt;`. - For sufficiently complex apps, we could build a bunch of different useful "services" that loop on channels to update the app. -```rust +```rust, no_run let profile = use_coroutine(cx, profile_service); let editor = use_coroutine(cx, editor_service); let sync = use_coroutine(cx, sync_service); @@ -143,9 +140,9 @@ async fn editor_service(rx: UnboundedReceiver) { } ``` -We can combine coroutines with [Fermi](https://docs.rs/fermi/latest/fermi/index.html) to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state *within* a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your *actual* state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI. +We can combine coroutines with [Fermi](https://docs.rs/fermi/latest/fermi/index.html) to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state _within_ a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your _actual_ state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI. -```rust +```rust, no_run static USERNAME: Atom = |_| "default".to_string(); fn app(cx: Scope) -> Element { @@ -169,7 +166,7 @@ fn Banner(cx: Scope) -> Element { Now, in our sync service, we can structure our state however we want. We only need to update the view values when ready. -```rust +```rust, no_run use futures_util::stream::StreamExt; enum SyncAction { @@ -198,7 +195,7 @@ async fn sync_service(mut rx: UnboundedReceiver, atoms: AtomRoot) { Coroutine handles are automatically injected through the context API. You can use the `use_coroutine_handle` hook with the message type as a generic to fetch a handle. -```rust +```rust, no_run fn Child(cx: Scope) -> Element { let sync_task = use_coroutine_handle::(cx); diff --git a/docs/guide/src/en/async/use_future.md b/docs/guide/src/en/async/use_future.md index 864dd5acb..c7b262c61 100644 --- a/docs/guide/src/en/async/use_future.md +++ b/docs/guide/src/en/async/use_future.md @@ -4,21 +4,20 @@ For example, we can make an API request (using [reqwest](https://docs.rs/reqwest/latest/reqwest/index.html)) inside `use_future`: -```rust +```rust, no_run {{#include ../../../examples/use_future.rs:use_future}} ``` The code inside `use_future` will be submitted to the Dioxus scheduler once the component has rendered. -We can use `.value()` to get the result of the future. On the first run, since there's no data ready when the component loads, its value will be `None`. However, once the future is finished, the component will be re-rendered and the value will now be `Some(...)`, containing the return value of the closure. +We can use `.value()` to get the result of the future. On the first run, since there's no data ready when the component loads, its value will be `None`. However, once the future is finished, the component will be re-rendered and the value will now be `Some(...)`, containing the return value of the closure. We can then render that result: -```rust +```rust, no_run {{#include ../../../examples/use_future.rs:render}} ``` - ## Restarting the Future The `UseFuture` handle provides a `restart` method. It can be used to execute the future again, producing a new value. @@ -27,7 +26,6 @@ The `UseFuture` handle provides a `restart` method. It can be used to execute th Often, you will need to run the future again every time some value (e.g. a prop) changes. Rather than calling `restart` manually, you can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. Example: - -```rust +```rust, no_run {{#include ../../../examples/use_future.rs:dependency}} ``` diff --git a/docs/guide/src/en/best_practices/antipatterns.md b/docs/guide/src/en/best_practices/antipatterns.md index 873af6e39..771ffe75f 100644 --- a/docs/guide/src/en/best_practices/antipatterns.md +++ b/docs/guide/src/en/best_practices/antipatterns.md @@ -8,7 +8,7 @@ Fragments don't mount a physical element to the DOM immediately, so Dioxus must Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing an API for registering shared state without the Context Provider pattern. -```rust +```rust, no_run {{#include ../../../examples/anti_patterns.rs:nested_fragments}} ``` @@ -16,7 +16,7 @@ Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigate As described in the [dynamic rendering chapter](../interactivity/dynamic_rendering.md#the-key-attribute), list items must have unique keys that are associated with the same items across renders. This helps Dioxus associate state with the contained components and ensures good diffing performance. Do not omit keys, unless you know that the list will never change. -```rust +```rust, no_run {{#include ../../../examples/anti_patterns.rs:iter_keys}} ``` @@ -30,4 +30,4 @@ Suppose you have a struct `User` containing the field `username: String`. If you Every time you update the state, Dioxus needs to re-render the component – this is inefficient! Consider refactoring your code to avoid this. -Also, if you unconditionally update the state during render, it will be re-rendered in an infinite loop. \ No newline at end of file +Also, if you unconditionally update the state during render, it will be re-rendered in an infinite loop. diff --git a/docs/guide/src/en/best_practices/error_handling.md b/docs/guide/src/en/best_practices/error_handling.md index afbb45417..fad8f1c10 100644 --- a/docs/guide/src/en/best_practices/error_handling.md +++ b/docs/guide/src/en/best_practices/error_handling.md @@ -4,23 +4,21 @@ A selling point of Rust for web development is the reliability of always knowing However, we haven't talked about error handling at all in this guide! In this chapter, we'll cover some strategies in handling errors to ensure your app never crashes. - - ## The simplest – returning None Astute observers might have noticed that `Element` is actually a type alias for `Option`. You don't need to know what a `VNode` is, but it's important to recognize that we could actually return nothing at all: -```rust +```rust, no_run fn App(cx: Scope) -> Element { None } ``` -This lets us add in some syntactic sugar for operations we think *shouldn't* fail, but we're still not confident enough to "unwrap" on. +This lets us add in some syntactic sugar for operations we think _shouldn't_ fail, but we're still not confident enough to "unwrap" on. > The nature of `Option` might change in the future as the `try` trait gets upgraded. -```rust +```rust, no_run fn App(cx: Scope) -> Element { // immediately return "None" let name = cx.use_hook(|_| Some("hi"))?; @@ -31,7 +29,7 @@ fn App(cx: Scope) -> Element { Because Rust can't accept both Options and Results with the existing try infrastructure, you'll need to manually handle Results. This can be done by converting them into Options or by explicitly handling them. -```rust +```rust, no_run fn App(cx: Scope) -> Element { // Convert Result to Option let name = cx.use_hook(|_| "1.234").parse().ok()?; @@ -46,8 +44,7 @@ fn App(cx: Scope) -> Element { } ``` -Notice that while hooks in Dioxus do not like being called in conditionals or loops, they *are* okay with early returns. Returning an error state early is a completely valid way of handling errors. - +Notice that while hooks in Dioxus do not like being called in conditionals or loops, they _are_ okay with early returns. Returning an error state early is a completely valid way of handling errors. ## Match results @@ -55,14 +52,13 @@ The next "best" way of handling errors in Dioxus is to match on the error locall To do this, we simply have an error state built into our component: -```rust +```rust, no_run let err = use_state(cx, || None); ``` Whenever we perform an action that generates an error, we'll set that error state. We can then match on the error in a number of ways (early return, return Element, etc). - -```rust +```rust, no_run fn Commandline(cx: Scope) -> Element { let error = use_state(cx, || None); @@ -83,7 +79,7 @@ fn Commandline(cx: Scope) -> Element { If you're dealing with a handful of components with minimal nesting, you can just pass the error handle into child components. -```rust +```rust, no_run fn Commandline(cx: Scope) -> Element { let error = use_state(cx, || None); @@ -110,8 +106,7 @@ To get started, consider using a built-in hook like `use_context` and `use_conte At the "top" of our architecture, we're going to want to explicitly declare a value that could be an error. - -```rust +```rust, no_run enum InputError { None, TooLong, @@ -123,7 +118,7 @@ static INPUT_ERROR: Atom = |_| InputError::None; Then, in our top level component, we want to explicitly handle the possible error state for this part of the tree. -```rust +```rust, no_run fn TopLevel(cx: Scope) -> Element { let error = use_read(cx, INPUT_ERROR); @@ -137,7 +132,7 @@ fn TopLevel(cx: Scope) -> Element { Now, whenever a downstream component has an error in its actions, it can simply just set its own error state: -```rust +```rust, no_run fn Commandline(cx: Scope) -> Element { let set_error = use_set(cx, INPUT_ERROR); diff --git a/docs/guide/src/en/contributing/walkthrough_readme.md b/docs/guide/src/en/contributing/walkthrough_readme.md index 9165d65a8..5334630c9 100644 --- a/docs/guide/src/en/contributing/walkthrough_readme.md +++ b/docs/guide/src/en/contributing/walkthrough_readme.md @@ -6,7 +6,7 @@ This walkthrough will take you through the internals of the Hello World example We start will a hello world program. This program renders a desktop app with the text "Hello World" in a webview. -```rust +```rust, no_run {{#include ../../../../../examples/readme.rs}} ``` @@ -16,7 +16,7 @@ We start will a hello world program. This program renders a desktop app with the Before the Rust compiler runs the program, it will expand all macros. Here is what the hello world example looks like expanded: -```rust +```rust, no_run {{#include ../../../examples/readme_expanded.rs}} ``` @@ -40,7 +40,7 @@ Before we dive into the initial render in the virtual dom, we need to discuss wh The Virtual Dom roughly looks like this: -```rust +```rust, no_run pub struct VirtualDom { // All the templates that have been created or set durring hot reloading pub(crate) templates: FxHashMap>>, diff --git a/docs/guide/src/en/custom_renderer/index.md b/docs/guide/src/en/custom_renderer/index.md index b531593dc..3b6888b0d 100644 --- a/docs/guide/src/en/custom_renderer/index.md +++ b/docs/guide/src/en/custom_renderer/index.md @@ -25,7 +25,7 @@ Dioxus is built around the concept of [Templates](https://docs.rs/dioxus-core/la The `Mutation` type is a serialized enum that represents an operation that should be applied to update the UI. The variants roughly follow this set: -```rust +```rust, no_run enum Mutation { AppendChildren, AssignId, @@ -58,7 +58,7 @@ Whenever a `CreateElement` edit is generated during diffing, Dioxus increments i For the sake of understanding, let's consider this example – a very simple UI declaration: -```rust +```rust, no_run rsx!( h1 {"count: {x}"} ) ``` @@ -68,7 +68,7 @@ The above rsx will create a template that contains one static h1 tag and a place The template will look something like this: -```rust +```rust, no_run Template { // Some id that is unique for the entire project name: "main.rs:1:1:0", @@ -118,7 +118,7 @@ After the renderer has created all of the new templates, it can begin to process When the renderer starts, it should contain the Root node on the stack and store the Root node with an id of 0. The Root node is the top-level node of the UI. In HTML, this is the `
` element. -```rust +```rust, no_run instructions: [] stack: [ RootNode, @@ -130,7 +130,7 @@ nodes: [ The first mutation is a `LoadTemplate` mutation. This tells the renderer to load a root from the template with the given id. The renderer will then push the root node of the template onto the stack and store it with an id for later. In this case, the root node is an h1 element. -```rust +```rust, no_run instructions: [ LoadTemplate { // the id of the template @@ -153,7 +153,7 @@ nodes: [ Next, Dioxus will create the dynamic text node. The diff algorithm decides that this node needs to be created, so Dioxus will generate the Mutation `HydrateText`. When the renderer receives this instruction, it will navigate to the placeholder text node in the template and replace it with the new text. -```rust +```rust, no_run instructions: [ LoadTemplate { name: "main.rs:1:1:0", @@ -180,7 +180,7 @@ nodes: [ Remember, the h1 node is not attached to anything (it is unmounted) so Dioxus needs to generate an Edit that connects the h1 node to the Root. It depends on the situation, but in this case, we use `AppendChildren`. This pops the text node off the stack, leaving the Root element as the next element on the stack. -```rust +```rust, no_run instructions: [ LoadTemplate { name: "main.rs:1:1:0", @@ -210,7 +210,7 @@ nodes: [ Over time, our stack looked like this: -```rust +```rust, no_run [Root] [Root,

""

] [Root,

"count: 0"

] @@ -229,7 +229,7 @@ Like most GUIs, Dioxus relies on an event loop to progress the VirtualDOM. The V The code for the WebSys implementation is straightforward, so we'll add it here to demonstrate how simple an event loop is: -```rust, ignore +```rust, no_run, ignore pub async fn run(&mut self) -> dioxus_core::error::Result<()> { // Push the body element onto the WebsysDom's stack machine let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom()); @@ -259,7 +259,7 @@ pub async fn run(&mut self) -> dioxus_core::error::Result<()> { It's important to decode what the real events are for your event system into Dioxus' synthetic event system (synthetic meaning abstracted). This simply means matching your event type and creating a Dioxus `UserEvent` type. Right now, the virtual event system is modeled almost entirely around the HTML spec, but we are interested in slimming it down. -```rust, ignore +```rust, no_run, ignore fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { match event.type_().as_str() { "keydown" => { @@ -308,7 +308,7 @@ The `RealDom` is a higher-level abstraction over updating the Dom. It uses an en Let's build a toy renderer with borders, size, and text color. Before we start let's take a look at an example element we can render: -```rust +```rust, no_run cx.render(rsx!{ div{ color: "red", @@ -380,19 +380,19 @@ To help in building a Dom, native-core provides the State trait and a RealDom st Native Core cannot create all of the required methods for the State trait, but it can derive some of them. To implement the State trait, you must implement the following methods and let the `#[partial_derive_state]` macro handle the rest: -```rust, ignore +```rust, no_run, ignore {{#include ../../../examples/custom_renderer.rs:derive_state}} ``` Lets take a look at how to implement the State trait for a simple renderer. -```rust +```rust, no_run {{#include ../../../examples/custom_renderer.rs:state_impl}} ``` Now that we have our state, we can put it to use in our RealDom. We can update the RealDom with apply_mutations to update the structure of the dom (adding, removing, and changing properties of nodes) and then update_state to update the States for each of the nodes that changed. -```rust +```rust, no_run {{#include ../../../examples/custom_renderer.rs:rendering}} ``` @@ -404,7 +404,7 @@ For most platforms, the layout of the Elements will stay the same. The [layout_a To make it easier to implement text editing in rust renderers, `native-core` also contains a renderer-agnostic cursor system. The cursor can handle text editing, selection, and movement with common keyboard shortcuts integrated. -```rust +```rust, no_run {{#include ../../../examples/custom_renderer.rs:cursor}} ``` diff --git a/docs/guide/src/en/describing_ui/component_children.md b/docs/guide/src/en/describing_ui/component_children.md index 02be25cd5..7e329abbc 100644 --- a/docs/guide/src/en/describing_ui/component_children.md +++ b/docs/guide/src/en/describing_ui/component_children.md @@ -2,13 +2,13 @@ In some cases, you may wish to create a component that acts as a container for some other content, without the component needing to know what that content is. To achieve this, create a prop of type `Element`: -```rust +```rust, no_run {{#include ../../../examples/component_element_props.rs:Clickable}} ``` Then, when rendering the component, you can pass in the output of `cx.render(rsx!(...))`: -```rust +```rust, no_run {{#include ../../../examples/component_element_props.rs:Clickable_usage}} ``` @@ -20,12 +20,12 @@ Then, when rendering the component, you can pass in the output of `cx.render(rsx Rather than passing the RSX through a regular prop, you may wish to accept children similarly to how elements can have children. The "magic" `children` prop lets you achieve this: -```rust +```rust, no_run {{#include ../../../examples/component_children.rs:Clickable}} ``` This makes using the component much simpler: simply put the RSX inside the `{}` brackets – and there is no need for a `render` call or another macro! -```rust +```rust, no_run {{#include ../../../examples/component_children.rs:Clickable_usage}} ``` diff --git a/docs/guide/src/en/describing_ui/component_props.md b/docs/guide/src/en/describing_ui/component_props.md index 9c5cd4be3..947ec5f70 100644 --- a/docs/guide/src/en/describing_ui/component_props.md +++ b/docs/guide/src/en/describing_ui/component_props.md @@ -7,6 +7,7 @@ Just like you can pass arguments to a function, you can pass props to a componen Component props are a single struct annotated with `#[derive(Props)]`. For a component to accept props, the type of its argument must be `Scope`. Then, you can access the value of the props using `cx.props`. There are 2 flavors of Props structs: + - Owned props: - Don't have an associated lifetime - Implement `PartialEq`, allow for memoization (if the props don't change, Dioxus won't re-render the component) @@ -14,17 +15,17 @@ There are 2 flavors of Props structs: - [Borrow](https://doc.rust-lang.org/beta/rust-by-example/scope/borrow.html) from a parent component - Cannot be memoized due to lifetime constraints - ### Owned Props Owned Props are very simple – they don't borrow anything. Example: -```rust +```rust, no_run {{#include ../../../examples/component_owned_props.rs:Likes}} ``` You can then pass prop values to the component the same way you would pass attributes to an element: -```rust + +```rust, no_run {{#include ../../../examples/component_owned_props.rs:App}} ``` @@ -36,18 +37,19 @@ Owned props work well if your props are easy to copy around – like a single nu Rust allows for something more efficient – borrowing the String as a `&str` – this is what Borrowed Props are for! -```rust +```rust, no_run {{#include ../../../examples/component_borrowed_props.rs:TitleCard}} ``` We can then use the component like this: -```rust +```rust, no_run {{#include ../../../examples/component_borrowed_props.rs:App}} ``` + ![Screenshot: TitleCard component](./images/component_borrowed_props_screenshot.png) -Borrowed props can be very useful, but they do not allow for memorization so they will *always* rerun when the parent scope is rerendered. Because of this Borrowed Props should be reserved for components that are cheap to rerun or places where cloning data is an issue. Using Borrowed Props everywhere will result in large parts of your app rerunning every interaction. +Borrowed props can be very useful, but they do not allow for memorization so they will _always_ rerun when the parent scope is rerendered. Because of this Borrowed Props should be reserved for components that are cheap to rerun or places where cloning data is an issue. Using Borrowed Props everywhere will result in large parts of your app rerunning every interaction. ## Prop Options @@ -57,13 +59,13 @@ The `#[derive(Props)]` macro has some features that let you customize the behavi You can create optional fields by using the `Option<…>` type for a field: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:OptionalProps}} ``` Then, you can choose to either provide them or not: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:OptionalProps_usage}} ``` @@ -71,13 +73,13 @@ Then, you can choose to either provide them or not: If you want to explicitly require an `Option`, and not an optional prop, you can annotate it with `#[props(!optional)]`: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:ExplicitOption}} ``` Then, you have to explicitly pass either `Some("str")` or `None`: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:ExplicitOption_usage}} ``` @@ -85,13 +87,13 @@ Then, you have to explicitly pass either `Some("str")` or `None`: You can use `#[props(default = 42)]` to make a field optional and specify its default value: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:DefaultComponent}} ``` Then, similarly to optional props, you don't have to provide it: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:DefaultComponent_usage}} ``` @@ -99,13 +101,13 @@ Then, similarly to optional props, you don't have to provide it: It is common for Rust functions to accept `impl Into` rather than just `SomeType` to support a wider range of parameters. If you want similar functionality with props, you can use `#[props(into)]`. For example, you could add it on a `String` prop – and `&str` will also be automatically accepted, as it can be converted into `String`: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:IntoComponent}} ``` Then, you can use it so: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:IntoComponent_usage}} ``` @@ -115,7 +117,7 @@ So far, every Component function we've seen had a corresponding ComponentProps s `inline_props` allows you to do just that. Instead of typing the "full" version: -```rust +```rust, no_run #[derive(Props, PartialEq)] struct TitleCardProps { title: String, @@ -130,7 +132,7 @@ fn TitleCard(cx: Scope) -> Element { ...you can define a function that accepts props as arguments. Then, just annotate it with `#[inline_props]`, and the macro will turn it into a regular Component for you: -```rust +```rust, no_run #[inline_props] fn TitleCard(cx: Scope, title: String) -> Element { cx.render(rsx!{ diff --git a/docs/guide/src/en/describing_ui/components.md b/docs/guide/src/en/describing_ui/components.md index 9da0d6605..2b72fcaf2 100644 --- a/docs/guide/src/en/describing_ui/components.md +++ b/docs/guide/src/en/describing_ui/components.md @@ -4,7 +4,7 @@ Just like you wouldn't want to write a complex program in a single, long, `main` A component is a Rust function, named in UpperCammelCase, that takes a `Scope` parameter and returns an `Element` describing the UI it wants to render. In fact, our `App` function is a component! -```rust +```rust, no_run {{#include ../../../examples/hello_world_desktop.rs:component}} ``` @@ -12,13 +12,13 @@ A component is a Rust function, named in UpperCammelCase, that takes a `Scope` p A Component is responsible for some rendering task – typically, rendering an isolated part of the user interface. For example, you could have an `About` component that renders a short description of Dioxus Labs: -```rust +```rust, no_run {{#include ../../../examples/components.rs:About}} ``` Then, you can render your component in another component, similarly to how elements are rendered: -```rust +```rust, no_run {{#include ../../../examples/components.rs:App}} ``` diff --git a/docs/guide/src/en/describing_ui/index.md b/docs/guide/src/en/describing_ui/index.md index 808e6af3c..8e5b3fe8f 100644 --- a/docs/guide/src/en/describing_ui/index.md +++ b/docs/guide/src/en/describing_ui/index.md @@ -1,35 +1,42 @@ # Describing the UI -Dioxus is a *declarative* framework. This means that instead of telling Dioxus what to do (e.g. to "create an element" or "set the color to red") we simply *declare* what we want the UI to look like using RSX. +Dioxus is a _declarative_ framework. This means that instead of telling Dioxus what to do (e.g. to "create an element" or "set the color to red") we simply _declare_ what we want the UI to look like using RSX. You have already seen a simple example of RSX syntax in the "hello world" application: -```rust +```rust, no_run {{#include ../../../examples/hello_world_desktop.rs:component}} ``` -Here, we use the `rsx!` macro to *declare* that we want a `div` element, containing the text `"Hello, world!"`. Dioxus takes the RSX and constructs a UI from it. +Here, we use the `rsx!` macro to _declare_ that we want a `div` element, containing the text `"Hello, world!"`. Dioxus takes the RSX and constructs a UI from it. ## RSX Features RSX is very similar to HTML in that it describes elements with attributes and children. Here's an empty `div` element in RSX, as well as the resulting HTML: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:empty}} ``` + ```html
``` - ### Attributes Attributes (and [listeners](../interactivity/index.md)) modify the behavior or appearance of the element they are attached to. They are specified inside the `{}` brackets, using the `name: value` syntax. You can provide the value as a literal in the RSX: -```rust + +```rust, no_run {{#include ../../../examples/rsx_overview.rs:attributes}} ``` + ```html -
+ ``` > Note: All attributes defined in `dioxus-html` follow the snake_case naming convention. They transform their `snake_case` names to HTML's `camelCase` attributes. @@ -40,26 +47,27 @@ Attributes (and [listeners](../interactivity/index.md)) modify the behavior or a Dioxus has a pre-configured set of attributes that you can use. RSX is validated at compile time to make sure you didn't specify an invalid attribute. If you want to override this behavior with a custom attribute name, specify the attribute in quotes: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:custom_attributes}} ``` + ```html - - + ``` ### Interpolation Similarly to how you can [format](https://doc.rust-lang.org/rust-by-example/hello/print/fmt.html) Rust strings, you can also interpolate in RSX text. Use `{variable}` to Display the value of a variable in a string, or `{variable:?}` to use the Debug representation: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:formatting}} ``` + ```html
-
ES
-
42
-
{}
+
ES
+
42
+
{}
``` @@ -67,14 +75,15 @@ Similarly to how you can [format](https://doc.rust-lang.org/rust-by-example/hell To add children to an element, put them inside the `{}` brackets after all attributes and listeners in the element. They can be other elements, text, or [components](components.md). For example, you could have an `ol` (ordered list) element, containing 3 `li` (list item) elements, each of which contains some text: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:children}} ``` + ```html
    -
  1. First Item
  2. -
  3. Second Item
  4. -
  5. Third Item
  6. +
  7. First Item
  8. +
  9. Second Item
  10. +
  11. Third Item
``` @@ -82,7 +91,7 @@ To add children to an element, put them inside the `{}` brackets after all attri You can render multiple elements at the top level of `rsx!` and they will be automatically grouped. -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:manyroots}} ``` @@ -95,9 +104,10 @@ You can render multiple elements at the top level of `rsx!` and they will be aut You can include arbitrary Rust expressions as children within RSX that implements [IntoDynNode](https://docs.rs/dioxus-core/0.3/dioxus_core/trait.IntoDynNode.html). This is useful for displaying data from an [iterator](https://doc.rust-lang.org/stable/book/ch13-02-iterators.html#processing-a-series-of-items-with-iterators): -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:expression}} ``` + ```html DIOXUS0123456789 ``` @@ -106,9 +116,10 @@ You can include arbitrary Rust expressions as children within RSX that implement In addition to iterators you can also use for loops directly within RSX: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:loops}} ``` + ```html
0
1
@@ -122,9 +133,10 @@ In addition to iterators you can also use for loops directly within RSX: You can also use if statements without an else branch within RSX: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:ifstatements}} ``` + ```html
true
-``` \ No newline at end of file +``` diff --git a/docs/guide/src/en/describing_ui/special_attributes.md b/docs/guide/src/en/describing_ui/special_attributes.md index 7bed1ef94..4ba7a40f1 100644 --- a/docs/guide/src/en/describing_ui/special_attributes.md +++ b/docs/guide/src/en/describing_ui/special_attributes.md @@ -8,8 +8,7 @@ If you're working with pre-rendered assets, output from templates, or output fro For example, shipping a markdown-to-Dioxus converter might significantly bloat your final application size. Instead, you'll want to pre-render your markdown to HTML and then include the HTML directly in your output. We use this approach for the [Dioxus homepage](https://dioxuslabs.com): - -```rust +```rust, no_run {{#include ../../../examples/dangerous_inner_html.rs:dangerous_inner_html}} ``` @@ -17,21 +16,21 @@ For example, shipping a markdown-to-Dioxus converter might significantly bloat y > > If you're handling untrusted input, make sure to sanitize your HTML before passing it into `dangerous_inner_html` – or just pass it to a Text Element to escape any HTML tags. - ## Boolean Attributes Most attributes, when rendered, will be rendered exactly as the input you provided. However, some attributes are considered "boolean" attributes and just their presence determines whether they affect the output. For these attributes, a provided value of `"false"` will cause them to be removed from the target element. So this RSX wouldn't actually render the `hidden` attribute: -```rust +```rust, no_run {{#include ../../../examples/boolean_attribute.rs:boolean_attribute}} ``` + ```html
hello
``` -Not all attributes work like this however. *Only the following attributes* have this behavior: +Not all attributes work like this however. _Only the following attributes_ have this behavior: - `allowfullscreen` - `allowpaymentrequest` diff --git a/docs/guide/src/en/fullstack/getting_started.md b/docs/guide/src/en/fullstack/getting_started.md index 8e753e2c9..9511b8a6c 100644 --- a/docs/guide/src/en/fullstack/getting_started.md +++ b/docs/guide/src/en/fullstack/getting_started.md @@ -39,7 +39,7 @@ tokio = { version = "*", features = ["full"] } Now, set up your Axum app to serve the Dioxus app. -```rust +```rust, no_run {{#include ../../../examples/server_basic.rs}} ``` @@ -72,7 +72,7 @@ web = ["dioxus-web"] Next, we need to modify our `main.rs` to use either hydrate on the client or render on the server depending on the active features. -```rust +```rust, no_run {{#include ../../../examples/hydration.rs}} ``` @@ -95,7 +95,7 @@ To do this, we must remove the serve_dioxus_application and replace it with a cu The only thing we need to change on the client is the props. `dioxus-fullstack` will automatically serialize the props it uses to server render the app and send them to the client. In the client section of `main.rs`, we need to add `get_root_props_from_document` to deserialize the props before we hydrate the app. -```rust +```rust, no_run {{#include ../../../examples/hydration_props.rs}} ``` diff --git a/docs/guide/src/en/fullstack/server_functions.md b/docs/guide/src/en/fullstack/server_functions.md index b9d40ed2c..57883885a 100644 --- a/docs/guide/src/en/fullstack/server_functions.md +++ b/docs/guide/src/en/fullstack/server_functions.md @@ -20,7 +20,7 @@ cargo add serde Next, add the server function to your `main.rs`: -```rust +```rust, no_run {{#include ../../../examples/server_function.rs}} ``` diff --git a/docs/guide/src/en/getting_started/desktop.md b/docs/guide/src/en/getting_started/desktop.md index b24215218..42fe5c1a2 100644 --- a/docs/guide/src/en/getting_started/desktop.md +++ b/docs/guide/src/en/getting_started/desktop.md @@ -72,6 +72,6 @@ cargo add dioxus-desktop Edit your `main.rs`: -```rust +```rust, no_run {{#include ../../../examples/hello_world_desktop.rs:all}} ``` diff --git a/docs/guide/src/en/getting_started/hot_reload.md b/docs/guide/src/en/getting_started/hot_reload.md index 2ebf5e4aa..b14de30a4 100644 --- a/docs/guide/src/en/getting_started/hot_reload.md +++ b/docs/guide/src/en/getting_started/hot_reload.md @@ -36,7 +36,7 @@ For more information about hot reloading on native platforms and configuration o Add the following to your main function: -```rust +```rust, no_run fn main() { hot_reload_init!(); // launch your application diff --git a/docs/guide/src/en/getting_started/liveview.md b/docs/guide/src/en/getting_started/liveview.md index 54f792e74..56847cab5 100644 --- a/docs/guide/src/en/getting_started/liveview.md +++ b/docs/guide/src/en/getting_started/liveview.md @@ -1,18 +1,17 @@ # Liveview -Liveview allows apps to *run* on the server and *render* in the browser. It uses WebSockets to communicate between the server and the browser. +Liveview allows apps to _run_ on the server and _render_ in the browser. It uses WebSockets to communicate between the server and the browser. Examples: + - [Axum Example](https://github.com/DioxusLabs/dioxus/tree/master/packages/liveview/examples/axum.rs) - [Salvo Example](https://github.com/DioxusLabs/dioxus/tree/master/packages/liveview/examples/salvo.rs) - [Warp Example](https://github.com/DioxusLabs/dioxus/tree/master/packages/liveview/examples/warp.rs) - ## Support Liveview is currently limited in capability when compared to the Web platform. Liveview apps run on the server in a native thread. This means that browser APIs are not available, so rendering WebGL, Canvas, etc is not as easy as the Web. However, native system APIs are accessible, so streaming, WebSockets, filesystem, etc are all viable APIs. - ## Setup For this guide, we're going to show how to use Dioxus Liveview with [Axum](https://docs.rs/axum/latest/axum/). @@ -50,17 +49,14 @@ tokio = { version = "1.15.0", features = ["full"] } Now, set up your Axum app to respond on an endpoint. - -```rust +```rust, no_run {{#include ../../../examples/hello_world_liveview.rs:glue}} ``` - And then add our app component: -```rust +```rust, no_run {{#include ../../../examples/hello_world_liveview.rs:app}} ``` And that's it! - diff --git a/docs/guide/src/en/getting_started/mobile.md b/docs/guide/src/en/getting_started/mobile.md index 2f93b807c..72d597f51 100644 --- a/docs/guide/src/en/getting_started/mobile.md +++ b/docs/guide/src/en/getting_started/mobile.md @@ -5,8 +5,8 @@ Build a mobile app with Dioxus! Example: [Todo App](https://github.com/DioxusLabs/example-projects/blob/master/ios_demo) ## Support -Mobile is currently the least-supported renderer target for Dioxus. Mobile apps are rendered with either the platform's WebView or experimentally through [WGPU](https://github.com/DioxusLabs/blitz). WebView doesn't support animations, transparency, and native widgets. +Mobile is currently the least-supported renderer target for Dioxus. Mobile apps are rendered with either the platform's WebView or experimentally through [WGPU](https://github.com/DioxusLabs/blitz). WebView doesn't support animations, transparency, and native widgets. Mobile support is currently best suited for CRUD-style apps, ideally for internal teams who need to develop quickly but don't care much about animations or native widgets. @@ -59,7 +59,7 @@ simple_logger = "*" Edit your `lib.rs`: -```rust +```rust, no_run use dioxus::prelude::*; fn main() { @@ -73,4 +73,4 @@ fn app(cx: Scope) -> Element { } }) } -``` \ No newline at end of file +``` diff --git a/docs/guide/src/en/getting_started/ssr.md b/docs/guide/src/en/getting_started/ssr.md index 7a1954191..080efbb6d 100644 --- a/docs/guide/src/en/getting_started/ssr.md +++ b/docs/guide/src/en/getting_started/ssr.md @@ -39,19 +39,19 @@ tokio = { version = "1.15.0", features = ["full"] } Now, set up your Axum app to respond on an endpoint. -```rust +```rust, no_run {{#include ../../../examples/hello_world_ssr.rs:main}} ``` And then add our endpoint. We can either render `rsx!` directly: -```rust +```rust, no_run {{#include ../../../examples/hello_world_ssr.rs:endpoint}} ``` Or we can render VirtualDoms. -```rust +```rust, no_run {{#include ../../../examples/hello_world_ssr.rs:second_endpoint}} ``` diff --git a/docs/guide/src/en/getting_started/tui.md b/docs/guide/src/en/getting_started/tui.md index 4e8d36cf9..504c01212 100644 --- a/docs/guide/src/en/getting_started/tui.md +++ b/docs/guide/src/en/getting_started/tui.md @@ -29,7 +29,7 @@ cargo add dioxus-tui Then, edit your `main.rs` with the basic template. -```rust +```rust, no_run {{#include ../../../examples/hello_world_tui.rs}} ``` @@ -41,6 +41,6 @@ cargo run Press "ctrl-c" to close the app. To switch from "ctrl-c" to just "q" to quit you can launch the app with a configuration to disable the default quit and use the root TuiContext to quit on your own. -```rust +```rust, no_run {{#include ../../../examples/hello_world_tui_no_ctrl_c.rs}} ``` diff --git a/docs/guide/src/en/getting_started/web.md b/docs/guide/src/en/getting_started/web.md index e8a1f3da2..32d4d3d0f 100644 --- a/docs/guide/src/en/getting_started/web.md +++ b/docs/guide/src/en/getting_started/web.md @@ -52,7 +52,7 @@ cargo add dioxus-web Edit your `main.rs`: -```rust +```rust, no_run {{#include ../../../examples/hello_world_web.rs}} ``` diff --git a/docs/guide/src/en/index.md b/docs/guide/src/en/index.md index 332d5957b..bbebb0bc9 100644 --- a/docs/guide/src/en/index.md +++ b/docs/guide/src/en/index.md @@ -4,7 +4,7 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust. This guide will help you get started with writing Dioxus apps for the Web, Desktop, Mobile, and more. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let mut count = use_state(cx, || 0); @@ -18,7 +18,7 @@ fn app(cx: Scope) -> Element { Dioxus is heavily inspired by React. If you know React, getting started with Dioxus will be a breeze. -> This guide assumes you already know some [Rust](https://www.rust-lang.org/)! If not, we recommend reading [*the book*](https://doc.rust-lang.org/book/ch01-00-getting-started.html) to learn Rust first. +> This guide assumes you already know some [Rust](https://www.rust-lang.org/)! If not, we recommend reading [_the book_](https://doc.rust-lang.org/book/ch01-00-getting-started.html) to learn Rust first. ## Features @@ -31,9 +31,10 @@ Dioxus is heavily inspired by React. If you know React, getting started with Dio ### Multiplatform -Dioxus is a *portable* toolkit, meaning the Core implementation can run anywhere with no platform-dependent linking. Unlike many other Rust frontend toolkits, Dioxus is not intrinsically linked to WebSys. In fact, every element and event listener can be swapped out at compile time. By default, Dioxus ships with the `html` feature enabled, but this can be disabled depending on your target renderer. +Dioxus is a _portable_ toolkit, meaning the Core implementation can run anywhere with no platform-dependent linking. Unlike many other Rust frontend toolkits, Dioxus is not intrinsically linked to WebSys. In fact, every element and event listener can be swapped out at compile time. By default, Dioxus ships with the `html` feature enabled, but this can be disabled depending on your target renderer. Right now, we have several 1st-party renderers: + - WebSys (for WASM): Great support - Tao/Tokio (for Desktop apps): Good support - Tao/Tokio (for Mobile apps): Poor support diff --git a/docs/guide/src/en/interactivity/custom_hooks.md b/docs/guide/src/en/interactivity/custom_hooks.md index f3acec269..d075bb632 100644 --- a/docs/guide/src/en/interactivity/custom_hooks.md +++ b/docs/guide/src/en/interactivity/custom_hooks.md @@ -10,13 +10,13 @@ To avoid repetition, you can encapsulate business logic based on existing hooks For example, if many components need to access an `AppSettings` struct, you can create a "shortcut" hook: -```rust +```rust, no_run {{#include ../../../examples/hooks_composed.rs:wrap_context}} ``` Or if you want to wrap a hook that persists reloads with the storage API, you can build on top of the use_ref hook to work with mutable state: -```rust +```rust, no_run {{#include ../../../examples/hooks_composed.rs:use_storage}} ``` @@ -34,7 +34,7 @@ Inside the initialization closure, you will typically make calls to other `cx` m Here is a simplified implementation of the `use_state` hook: -```rust +```rust, no_run {{#include ../../../examples/hooks_custom_logic.rs:use_state}} ``` @@ -42,7 +42,7 @@ Here is a simplified implementation of the `use_state` hook: Here is an implementation of the `use_context` and `use_context_provider` hooks: -```rust +```rust, no_run {{#include ../../../examples/hooks_custom_logic.rs:use_context}} ``` @@ -54,13 +54,13 @@ When writing a custom hook, you should avoid the following anti-patterns: This version of use_state may seem more efficient, but it is not cloneable: -```rust +```rust, no_run {{#include ../../../examples/hooks_anti_patterns.rs:non_clone_state}} ``` If we try to use this hook in an async block, we will get a compile error: -```rust +```rust, no_run fn FutureComponent(cx: &ScopeState) -> Element { let my_state = my_use_state(cx, || 0); cx.spawn({ @@ -76,7 +76,7 @@ fn FutureComponent(cx: &ScopeState) -> Element { But with the original version, we can use it in an async block: -```rust +```rust, no_run fn FutureComponent(cx: &ScopeState) -> Element { let my_state = use_state(cx, || 0); cx.spawn({ diff --git a/docs/guide/src/en/interactivity/dynamic_rendering.md b/docs/guide/src/en/interactivity/dynamic_rendering.md index 5c7ba1314..8d91650c3 100644 --- a/docs/guide/src/en/interactivity/dynamic_rendering.md +++ b/docs/guide/src/en/interactivity/dynamic_rendering.md @@ -6,7 +6,7 @@ Sometimes you want to render different things depending on the state/props. With To render different elements based on a condition, you could use an `if-else` statement: -```rust +```rust, no_run {{#include ../../../examples/conditional_rendering.rs:if_else}} ``` @@ -18,7 +18,7 @@ You may have noticed some repeated code in the `if-else` example above. Repeatin We can improve this example by splitting up the dynamic parts and inserting them where they are needed. -```rust +```rust, no_run {{#include ../../../examples/conditional_rendering.rs:if_else_improved}} ``` @@ -26,18 +26,17 @@ We can improve this example by splitting up the dynamic parts and inserting them Since `Element` is a `Option`, components accepting `Element` as a prop can inspect its contents, and render different things based on that. Example: -```rust +```rust, no_run {{#include ../../../examples/component_children_inspect.rs:Clickable}} ``` You can't mutate the `Element`, but if you need a modified version of it, you can construct a new one based on its attributes/children/etc. - ## Rendering Nothing To render nothing, you can return `None` from a component. This is useful if you want to conditionally hide something: -```rust +```rust, no_run {{#include ../../../examples/conditional_rendering.rs:conditional_none}} ``` @@ -58,7 +57,7 @@ For this, Dioxus accepts iterators that produce `Element`s. So we need to: Example: suppose you have a list of comments you want to render. Then, you can render them like this: -```rust +```rust, no_run {{#include ../../../examples/rendering_lists.rs:render_list}} ``` @@ -66,7 +65,7 @@ Example: suppose you have a list of comments you want to render. Then, you can r Because of how common it is to render a list of items, Dioxus provides a shorthand for this. Instead of using `.iter`, `.map`, and `rsx`, you can use a `for` loop with a body of rsx code: -```rust +```rust, no_run {{#include ../../../examples/rendering_lists.rs:render_list_for_loop}} ``` diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index e1057f98b..4c77cef1e 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -8,7 +8,7 @@ Event handlers are similar to regular attributes, but their name usually starts For example, to handle clicks on an element, we can specify an `onclick` handler: -```rust +```rust, no_run {{#include ../../../examples/event_click.rs:rsx}} ``` @@ -33,7 +33,7 @@ Some events will trigger first on the element the event originated at upward. Fo If you want to prevent this behavior, you can call `stop_propagation()` on the event: -```rust +```rust, no_run {{#include ../../../examples/event_nested.rs:rsx}} ``` @@ -43,25 +43,25 @@ Some events have a default behavior. For keyboard events, this might be entering In some instances, might want to avoid this default behavior. For this, you can add the `prevent_default` attribute with the name of the handler whose default behavior you want to stop. This attribute can be used for multiple handlers using their name separated by spaces: -```rust +```rust, no_run {{#include ../../../examples/event_prevent_default.rs:prevent_default}} ``` Any event handlers will still be called. -> Normally, in React or JavaScript, you'd call "preventDefault" on the event in the callback. Dioxus does *not* currently support this behavior. Note: this means you cannot conditionally prevent default behavior based on the data in the event. +> Normally, in React or JavaScript, you'd call "preventDefault" on the event in the callback. Dioxus does _not_ currently support this behavior. Note: this means you cannot conditionally prevent default behavior based on the data in the event. ## Handler Props Sometimes, you might want to make a component that accepts an event handler. A simple example would be a `FancyButton` component, which accepts an `on_click` handler: -```rust +```rust, no_run {{#include ../../../examples/event_handler_prop.rs:component_with_handler}} ``` Then, you can use it like any other handler: -```rust +```rust, no_run {{#include ../../../examples/event_handler_prop.rs:usage}} ``` diff --git a/docs/guide/src/en/interactivity/hooks.md b/docs/guide/src/en/interactivity/hooks.md index 083f04b49..6851aacbf 100644 --- a/docs/guide/src/en/interactivity/hooks.md +++ b/docs/guide/src/en/interactivity/hooks.md @@ -14,9 +14,10 @@ Hooks allow us to create state in our components. Hooks are Rust functions that For example, you might have seen the counter example, in which state (a number) is tracked using the `use_state` hook: -```rust +```rust, no_run {{#include ../../../examples/hooks_counter.rs:component}} ``` + ![Screenshot: counter app](./images/counter.png) Every time the component's state changes, it re-renders, and the component function is called, so you can describe what you want the new UI to look like. You don't have to worry about "changing" anything – just describe what you want in terms of the state, and Dioxus will take care of the rest! @@ -25,9 +26,10 @@ Every time the component's state changes, it re-renders, and the component funct You can use multiple hooks in the same component if you want: -```rust +```rust, no_run {{#include ../../../examples/hooks_counter_two_state.rs:component}} ``` + ![Screenshot: app with two counters](./images/counter_two_state.png) ## Rules of Hooks @@ -36,7 +38,7 @@ The above example might seem a bit magic, since Rust functions are typically not But how can Dioxus differentiate between multiple hooks in the same component? As you saw in the second example, both `use_state` functions were called with the same parameters, so how come they can return different things when the counters are different? -```rust +```rust, no_run {{#include ../../../examples/hooks_counter_two_state.rs:use_state_calls}} ``` @@ -51,17 +53,20 @@ This is only possible because the two hooks are always called in the same order, These rules mean that there are certain things you can't do with hooks: ### No Hooks in Conditionals -```rust + +```rust, no_run {{#include ../../../examples/hooks_bad.rs:conditional}} ``` ### No Hooks in Closures -```rust + +```rust, no_run {{#include ../../../examples/hooks_bad.rs:closure}} ``` ### No Hooks in Loops -```rust + +```rust, no_run {{#include ../../../examples/hooks_bad.rs:loop}} ``` @@ -75,9 +80,8 @@ Thankfully, there is another hook for that, `use_ref`! It is similar to `use_sta Here's a simple example that keeps a list of events in a `use_ref`. We can acquire write access to the state with `.with_mut()`, and then just `.push` a new value to the state: -```rust +```rust, no_run {{#include ../../../examples/hooks_use_ref.rs:component}} ``` > The return values of `use_state` and `use_ref` (`UseState` and `UseRef`, respectively) are in some ways similar to [`Cell`](https://doc.rust-lang.org/std/cell/) and [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) – they provide interior mutability. However, these Dioxus wrappers also ensure that the component gets re-rendered whenever you change the state. - diff --git a/docs/guide/src/en/interactivity/router.md b/docs/guide/src/en/interactivity/router.md index 4133e5e53..c5f8c7c36 100644 --- a/docs/guide/src/en/interactivity/router.md +++ b/docs/guide/src/en/interactivity/router.md @@ -4,7 +4,6 @@ In many of your apps, you'll want to have different "scenes". For a webpage, the To unify these platforms, Dioxus provides a first-party solution for scene management called Dioxus Router. - ## What is it? For an app like the Dioxus landing page (https://dioxuslabs.com), we want to have several different scenes: @@ -20,12 +19,11 @@ The Dioxus router makes it easy to create these scenes. To make sure we're using cargo add dioxus-router ``` - ## Using the router Unlike other routers in the Rust ecosystem, our router is built declaratively. This makes it possible to compose our app layout simply by arranging components. -```rust +```rust, no_run rsx!{ // All of our routes will be rendered inside this Router component Router { @@ -43,7 +41,7 @@ We can fix this one of two ways: - A fallback 404 page -```rust +```rust, no_run rsx!{ Router { Route { to: "/home", Home {} } @@ -54,10 +52,9 @@ rsx!{ } ``` - - Redirect 404 to home -```rust +```rust, no_run rsx!{ Router { Route { to: "/home", Home {} } @@ -72,8 +69,7 @@ rsx!{ For our app to navigate these routes, we can provide clickable elements called Links. These simply wrap `` elements that, when clicked, navigate the app to the given location. - -```rust +```rust, no_run rsx!{ Link { to: "/home", diff --git a/docs/guide/src/en/interactivity/sharing_state.md b/docs/guide/src/en/interactivity/sharing_state.md index 75ace8973..4c97e53bf 100644 --- a/docs/guide/src/en/interactivity/sharing_state.md +++ b/docs/guide/src/en/interactivity/sharing_state.md @@ -11,7 +11,8 @@ Suppose we want to build a meme editor. We want to have an input to edit the mem > Of course, in this simple example, we could write everything in one component – but it is better to split everything out in smaller components to make the code more reusable, maintainable, and performant (this is even more important for larger, complex apps). We start with a `Meme` component, responsible for rendering a meme with a given caption: -```rust + +```rust, no_run {{#include ../../../examples/meme_editor.rs:meme_component}} ``` @@ -19,14 +20,16 @@ We start with a `Meme` component, responsible for rendering a meme with a given We also create a caption editor, completely decoupled from the meme. The caption editor must not store the caption itself – otherwise, how will we provide it to the `Meme` component? Instead, it should accept the current caption as a prop, as well as an event handler to delegate input events to: -```rust +```rust, no_run {{#include ../../../examples/meme_editor.rs:caption_editor}} ``` Finally, a third component will render the other two as children. It will be responsible for keeping the state and passing down the relevant props. -```rust + +```rust, no_run {{#include ../../../examples/meme_editor.rs:meme_editor}} ``` + ![Meme Editor Screenshot: An old plastic skeleton sitting on a park bench. Caption: "me waiting for a language feature"](./images/meme_editor_screenshot.png) ## Using Context @@ -43,24 +46,26 @@ Dioxus offers a better solution than this "prop drilling" – providing context. First, we have to create a struct for our dark mode configuration: -```rust +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:DarkMode_struct}} ``` Now, in a top-level component (like `App`), we can provide the `DarkMode` context to all children components: -```rust + +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:context_provider}} ``` As a result, any child component of `App` (direct or not), can access the `DarkMode` context. -```rust + +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:use_context}} ``` > `use_context` returns `Option>` here. If the context has been provided, the value is `Some(UseSharedState)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`. For example, here's how we would implement the dark mode toggle, which both reads the context (to determine what color it should render) and writes to it (to toggle dark mode): -```rust + +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:toggle}} ``` - diff --git a/docs/guide/src/en/interactivity/user_input.md b/docs/guide/src/en/interactivity/user_input.md index e39dbcde5..6470588e5 100644 --- a/docs/guide/src/en/interactivity/user_input.md +++ b/docs/guide/src/en/interactivity/user_input.md @@ -6,11 +6,12 @@ Interfaces often need to provide a way to input data: e.g. text, numbers, checkb With controlled inputs, you are directly in charge of the state of the input. This gives you a lot of flexibility, and makes it easy to keep things in sync. For example, this is how you would create a controlled text input: -```rust +```rust, no_run {{#include ../../../examples/input_controlled.rs:component}} ``` Notice the flexibility – you can: + - Also display the same contents in another element, and they will be in sync - Transform the input every time it is modified (e.g. to make sure it is upper case) - Validate the input every time it changes @@ -23,9 +24,10 @@ As an alternative to controlled inputs, you can simply let the platform keep tra Since you don't necessarily have the current value of the uncontrolled input in state, you can access it either by listening to `oninput` events (similarly to controlled components), or, if the input is part of a form, you can access the form data in the form events (e.g. `oninput` or `onsubmit`): -```rust +```rust, no_run {{#include ../../../examples/input_uncontrolled.rs:component}} ``` + ``` Submitted! UiEvent { data: FormData { value: "", values: {"age": "very old", "date": "1966", "name": "Fred"} } } -``` \ No newline at end of file +``` diff --git a/docs/guide/src/pt-br/async/spawn.md b/docs/guide/src/pt-br/async/spawn.md index 95f7e2e3f..3c00934cf 100644 --- a/docs/guide/src/pt-br/async/spawn.md +++ b/docs/guide/src/pt-br/async/spawn.md @@ -2,7 +2,7 @@ Os **"hooks"** `use_future` e `use_coroutine` são úteis se você quiser gerar incondicionalmente o `Future`. Às vezes, porém, você desejará apenas gerar um `Future` em resposta a um evento, como um clique do mouse. Por exemplo, suponha que você precise enviar uma solicitação quando o usuário clicar em um botão "log in". Para isso, você pode usar `cx.spawn`: -```rust +```rust, no_run {{#include ../../../examples/spawn.rs:spawn}} ``` @@ -14,7 +14,7 @@ No entanto, como você normalmente precisa de uma maneira de atualizar o valor d Para tornar isso um pouco menos detalhado, o Dioxus exporta a macro `to_owned!` que criará uma ligação como mostrado acima, o que pode ser bastante útil ao lidar com muitos valores. -```rust +```rust, no_run {{#include ../../../examples/spawn.rs:to_owned_macro}} ``` @@ -24,6 +24,6 @@ Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the Às vezes, você pode querer gerar uma tarefa em segundo plano que precise de vários _threads_ ou conversar com o hardware que pode bloquear o código do seu aplicativo. Nesses casos, podemos gerar diretamente uma tarefa Tokio do nosso `Future`. Para Dioxus-Desktop, sua tarefa será gerada no tempo de execução Multi-Tarefado do Tokio: -```rust +```rust, no_run {{#include ../../../examples/spawn.rs:tokio}} ``` diff --git a/docs/guide/src/pt-br/async/use_coroutine.md b/docs/guide/src/pt-br/async/use_coroutine.md index cd7cbff11..d7edbd8cc 100644 --- a/docs/guide/src/pt-br/async/use_coroutine.md +++ b/docs/guide/src/pt-br/async/use_coroutine.md @@ -8,7 +8,7 @@ Assim como os `Futures` regulares, o código em uma corrotina Dioxus será execu A configuração básica para corrotinas é o _hook_ `use_coroutine`. A maioria das corrotinas que escrevemos serão _loops_ de pesquisa usando `async`/`await`. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let ws: &UseCoroutine<()> = use_coroutine(cx, |rx| async move { // Connect to some sort of service @@ -26,7 +26,7 @@ Para muitos serviços, um _loop_ assíncrono simples lidará com a maioria dos c No entanto, se quisermos desabilitar temporariamente a corrotina, podemos "pausá-la" usando o método `pause` e "retomá-la" usando o método `resume`: -```rust +```rust, no_run let sync: &UseCoroutine<()> = use_coroutine(cx, |rx| async move { // code for syncing }); @@ -56,7 +56,7 @@ Você deve ter notado que o encerramento `use_coroutine` recebe um argumento cha Usando corrotinas, temos a oportunidade de centralizar nossa lógica assíncrona. O parâmetro `rx` é um canal ilimitado para código externo à corrotina para enviar dados _para_ a corrotina. Em vez de fazer um _loop_ em um serviço externo, podemos fazer um _loop_ no próprio canal, processando mensagens de dentro de nosso aplicativo sem precisar gerar um novo `Future`. Para enviar dados para a corrotina, chamaríamos "send" no _handle_. -```rust +```rust, no_run enum ProfileUpdate { SetUsername(String), SetAge(i32) @@ -84,7 +84,7 @@ cx.render(rsx!{ Para aplicativos suficientemente complexos, poderíamos criar vários "serviços" úteis diferentes que fazem um _loop_ nos canais para atualizar o aplicativo. -```rust +```rust, no_run let profile = use_coroutine(cx, profile_service); let editor = use_coroutine(cx, editor_service); let sync = use_coroutine(cx, sync_service); @@ -104,7 +104,7 @@ async fn editor_service(rx: UnboundedReceiver) { Podemos combinar corrotinas com `Fermi` para emular o sistema `Thunk` do **Redux Toolkit** com muito menos dor de cabeça. Isso nos permite armazenar todo o estado do nosso aplicativo _dentro_ de uma tarefa e, em seguida, simplesmente atualizar os valores de "visualização" armazenados em `Atoms`. Não pode ser subestimado o quão poderosa é essa técnica: temos todas as vantagens das tarefas nativas do Rust com as otimizações e ergonomia do estado global. Isso significa que seu estado _real_ não precisa estar vinculado a um sistema como `Fermi` ou `Redux` – os únicos `Atoms` que precisam existir são aqueles que são usados para controlar a interface. -```rust +```rust, no_run static USERNAME: Atom = |_| "default".to_string(); fn app(cx: Scope) -> Element { @@ -128,7 +128,7 @@ fn Banner(cx: Scope) -> Element { Agora, em nosso serviço de sincronização, podemos estruturar nosso estado como quisermos. Só precisamos atualizar os valores da _view_ quando estiver pronto. -```rust +```rust, no_run enum SyncAction { SetUsername(String), } @@ -155,7 +155,7 @@ async fn sync_service(mut rx: UnboundedReceiver, atoms: AtomRoot) { Para obter valores de uma corrotina, basta usar um identificador `UseState` e definir o valor sempre que sua corrotina concluir seu trabalho. -```rust +```rust, no_run let sync_status = use_state(cx, || Status::Launching); let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| { to_owned![sync_status]; @@ -172,7 +172,7 @@ let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| { Os identificadores de corrotina são injetados automaticamente por meio da API de contexto. `use_coroutine_handle` com o tipo de mensagem como genérico pode ser usado para buscar um _handle_. -```rust +```rust, no_run fn Child(cx: Scope) -> Element { let sync_task = use_coroutine_handle::(cx); diff --git a/docs/guide/src/pt-br/async/use_future.md b/docs/guide/src/pt-br/async/use_future.md index 85f435e35..1e7030ddf 100644 --- a/docs/guide/src/pt-br/async/use_future.md +++ b/docs/guide/src/pt-br/async/use_future.md @@ -4,7 +4,7 @@ Por exemplo, podemos fazer uma solicitação de API dentro de `use_future`: -```rust +```rust, no_run {{#include ../../../examples/use_future.rs:use_future}} ``` @@ -14,7 +14,7 @@ Podemos usar `.value()` para obter o resultado do `Future`. Na primeira execuç Podemos então renderizar esse resultado: -```rust +```rust, no_run {{#include ../../../examples/use_future.rs:render}} ``` @@ -26,6 +26,6 @@ O identificador `UseFuture` fornece um método `restart`. Ele pode ser usado par Muitas vezes, você precisará executar o `Future` novamente toda vez que algum valor (por exemplo, uma prop) mudar. Ao invés de `.restart` manualmente, você pode fornecer uma tupla de "dependências" para o gancho. Ele executará automaticamente o `Future` quando qualquer uma dessas dependências for alterada. Exemplo: -```rust +```rust, no_run {{#include ../../../examples/use_future.rs:dependency}} ``` diff --git a/docs/guide/src/pt-br/best_practices/antipatterns.md b/docs/guide/src/pt-br/best_practices/antipatterns.md index 53fa71df0..fa57ba125 100644 --- a/docs/guide/src/pt-br/best_practices/antipatterns.md +++ b/docs/guide/src/pt-br/best_practices/antipatterns.md @@ -8,7 +8,7 @@ Os fragmentos não montam um elemento físico no DOM imediatamente, então o Dio Apenas os nós Componente e Fragmento são suscetíveis a esse problema. O Dioxus atenua isso com componentes fornecendo uma API para registrar o estado compartilhado sem o padrão _Context Provider_. -```rust +```rust, no_run {{#include ../../../examples/anti_patterns.rs:nested_fragments}} ``` @@ -16,7 +16,7 @@ Apenas os nós Componente e Fragmento são suscetíveis a esse problema. O Dioxu Conforme descrito no capítulo de renderização condicional, os itens da lista devem ter _keys_ exclusivas associadas aos mesmos itens nas renderizações. Isso ajuda o Dioxus a associar o estado aos componentes contidos e garante um bom desempenho de diferenciação. Não omita as _keys_, a menos que você saiba que a lista é estática e nunca será alterada. -```rust +```rust, no_run {{#include ../../../examples/anti_patterns.rs:iter_keys}} ``` diff --git a/docs/guide/src/pt-br/best_practices/error_handling.md b/docs/guide/src/pt-br/best_practices/error_handling.md index 938322234..7e027e9d0 100644 --- a/docs/guide/src/pt-br/best_practices/error_handling.md +++ b/docs/guide/src/pt-br/best_practices/error_handling.md @@ -8,7 +8,7 @@ No entanto, não falamos sobre tratamento de erros neste guia! Neste capítulo, Observadores astutos podem ter notado que `Element` é na verdade um alias de tipo para `Option`. Você não precisa saber o que é um `VNode`, mas é importante reconhecer que não poderíamos retornar nada: -```rust +```rust, no_run fn App(cx: Scope) -> Element { None } @@ -18,7 +18,7 @@ Isso nos permite adicionar um pouco de açúcar sintático para operações que > A natureza de `Option` pode mudar no futuro à medida que a característica `try` for atualizada. -```rust +```rust, no_run fn App(cx: Scope) -> Element { // immediately return "None" let name = cx.use_hook(|_| Some("hi"))?; @@ -29,7 +29,7 @@ fn App(cx: Scope) -> Element { Como o Rust não pode aceitar opções e resultados com a infraestrutura _try_ existente, você precisará manipular os resultados manualmente. Isso pode ser feito convertendo-os em `Option` ou manipulando-os explicitamente. -```rust +```rust, no_run fn App(cx: Scope) -> Element { // Convert Result to Option let name = cx.use_hook(|_| "1.234").parse().ok()?; @@ -52,13 +52,13 @@ A próxima "melhor" maneira de lidar com erros no Dioxus é combinar (`match`) o Para fazer isso, simplesmente temos um estado de erro embutido em nosso componente: -```rust +```rust, no_run let err = use_state(cx, || None); ``` Sempre que realizarmos uma ação que gere um erro, definiremos esse estado de erro. Podemos então combinar o erro de várias maneiras (retorno antecipado, elemento de retorno etc.). -```rust +```rust, no_run fn Commandline(cx: Scope) -> Element { let error = use_state(cx, || None); @@ -79,7 +79,7 @@ fn Commandline(cx: Scope) -> Element { Se você estiver lidando com alguns componentes com um mínimo de aninhamento, basta passar o identificador de erro para componentes filhos. -```rust +```rust, no_run fn Commandline(cx: Scope) -> Element { let error = use_state(cx, || None); @@ -106,7 +106,7 @@ Para começar, considere usar um _hook_ embutido como `use_context` e `use_conte No "topo" de nossa arquitetura, queremos declarar explicitamente um valor que pode ser um erro. -```rust +```rust, no_run enum InputError { None, TooLong, @@ -118,7 +118,7 @@ static INPUT_ERROR: Atom = |_| InputError::None; Então, em nosso componente de nível superior, queremos tratar explicitamente o possível estado de erro para esta parte da árvore. -```rust +```rust, no_run fn TopLevel(cx: Scope) -> Element { let error = use_read(cx, INPUT_ERROR); @@ -132,7 +132,7 @@ fn TopLevel(cx: Scope) -> Element { Agora, sempre que um componente _downstream_ tiver um erro em suas ações, ele pode simplesmente definir seu próprio estado de erro: -```rust +```rust, no_run fn Commandline(cx: Scope) -> Element { let set_error = use_set(cx, INPUT_ERROR); diff --git a/docs/guide/src/pt-br/custom_renderer/index.md b/docs/guide/src/pt-br/custom_renderer/index.md index 68881382a..195f9d6ca 100644 --- a/docs/guide/src/pt-br/custom_renderer/index.md +++ b/docs/guide/src/pt-br/custom_renderer/index.md @@ -21,7 +21,7 @@ Como referência, confira o interpretador `javascript` ou o renderizador `tui` c O tipo "DomEdit" é uma `enum` serializada que representa uma operação atômica que ocorre no `RealDom`. As variantes seguem aproximadamente este conjunto: -```rust +```rust, no_run enum DomEdit { PushRoot, AppendChildren, @@ -48,7 +48,7 @@ O mecanismo de diferenciação Dioxus opera como uma [máquina de pilha] (https: Para fins de compreensão, vamos considerar este exemplo – uma declaração de interface do usuário muito simples: -```rust +```rust, no_run rsx!( h1 {"hello world"} ) ``` @@ -56,7 +56,7 @@ Para começar, o Dioxus deve primeiro navegar até o contêiner dessa tag h1. Pa Quando o renderizador recebe essa instrução, ele empurra o `Node` real para sua própria pilha. A pilha do renderizador real ficará assim: -```rust +```rust, no_run instructions: [ PushRoot(Container) ] @@ -67,7 +67,7 @@ stack: [ Em seguida, o Dioxus encontrará o nó `h1`. O algoritmo `diff` decide que este nó precisa ser criado, então o Dioxus irá gerar o DomEdit `CreateElement`. Quando o renderizador receber esta instrução, ele criará um nó desmontado e o enviará para sua própria pilha (_stack_): -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -80,7 +80,7 @@ stack: [ Em seguida, Dioxus vê o nó de texto e gera o DomEdit `CreateTextNode`: -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -95,7 +95,7 @@ stack: [ Lembre-se, o nó de texto não está anexado a nada (ele está desmontado), então o Dioxus precisa gerar um _Edit_ que conecte o nó de texto ao elemento `h1`. Depende da situação, mas neste caso usamos `AppendChildren`. Isso remove o nó de texto da _stack_, deixando o elemento `h1` como o próximo elemento na linha. -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -110,7 +110,7 @@ stack: [ Chamamos `AppendChildren` novamente, retirando o nó `h1` e anexando-o ao pai: -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -125,7 +125,7 @@ stack: [ Finalmente, o contêiner é aberto, pois não precisamos mais dele. -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -139,7 +139,7 @@ stack: [] Com o tempo, nossa _stack_ ficou assim: -```rust +```rust, no_run [] [Container] [Container, h1] @@ -165,7 +165,7 @@ Como a maioria das GUIs, o Dioxus conta com um _loop_ de eventos para progredir O código para a implementação do `WebSys` é direto, então vamos adicioná-lo aqui para demonstrar como um `loop` de eventos é simples: -```rust +```rust, no_run pub async fn run(&mut self) -> dioxus_core::error::Result<()> { // Push the body element onto the WebsysDom's stack machine let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom()); @@ -195,7 +195,7 @@ pub async fn run(&mut self) -> dioxus_core::error::Result<()> { É importante que você decodifique os eventos reais do seu sistema de eventos no sistema de eventos sintético do Dioxus (entenda sintético como abstraído). Isso significa simplesmente combinar seu tipo de evento e criar um tipo Dioxus `UserEvent`. No momento, o sistema `VirtualEvent` é modelado quase inteiramente em torno da especificação HTML, mas estamos interessados em reduzi-lo. -```rust +```rust, no_run fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { match event.type_().as_str() { "keydown" => { @@ -233,7 +233,7 @@ Esses elementos personalizados são definidos como estruturas de unidade com imp Por exemplo, o elemento `div` é (aproximadamente!) definido assim: -```rust +```rust, no_run struct div; impl div { /// Some glorious documentation about the class property. @@ -262,7 +262,7 @@ O `RealDom` é uma abstração de nível superior sobre a atualização do Dom. Vamos construir um renderizador de exemplo com bordas, tamanho e cor do texto. Antes de começarmos, vamos dar uma olhada em um elemento de exemplo que podemos renderizar: -```rust +```rust, no_run cx.render(rsx!{ div{ color: "red", @@ -280,50 +280,50 @@ No diagrama a seguir, as setas representam o fluxo de dados: [![](https://mermaid.ink/img/pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw) -[//]: # '%% mermaid flow chart' -[//]: # 'flowchart TB' -[//]: # ' subgraph context' -[//]: # ' text_width(text width)' -[//]: # ' end' -[//]: # ' subgraph state' -[//]: # ' direction TB' -[//]: # ' subgraph div state' -[//]: # ' direction TB' -[//]: # ' state1(state)-->color1(color)' -[//]: # ' state1-->border1(border)' -[//]: # ' text_width-.->layout_width1(layout width)' -[//]: # ' linkStyle 2 stroke:#ff5500,stroke-width:4px;' -[//]: # ' state1-->layout_width1' -[//]: # ' end' -[//]: # ' subgraph p state' -[//]: # ' direction TB' -[//]: # ' state2(state)-->color2(color)' -[//]: # ' color1-.->color2' -[//]: # ' linkStyle 5 stroke:#0000ff,stroke-width:4px;' -[//]: # ' state2-->border2(border)' -[//]: # ' text_width-.->layout_width2(layout width)' -[//]: # ' linkStyle 7 stroke:#ff5500,stroke-width:4px;' -[//]: # ' state2-->layout_width2' -[//]: # ' layout_width2-.->layout_width1' -[//]: # ' linkStyle 9 stroke:#00aa00,stroke-width:4px;' -[//]: # ' end' -[//]: # ' subgraph hello world state' -[//]: # ' direction TB' -[//]: # ' state3(state)-->border3(border)' -[//]: # ' state3-->color3(color)' -[//]: # ' color2-.->color3' -[//]: # ' linkStyle 12 stroke:#0000ff,stroke-width:4px;' -[//]: # ' text_width-.->layout_width3(layout width)' -[//]: # ' linkStyle 13 stroke:#ff5500,stroke-width:4px;' -[//]: # ' state3-->layout_width3' -[//]: # ' layout_width3-.->layout_width2' -[//]: # ' linkStyle 15 stroke:#00aa00,stroke-width:4px;' -[//]: # ' end' -[//]: # ' end' +[//]: # "%% mermaid flow chart" +[//]: # "flowchart TB" +[//]: # " subgraph context" +[//]: # " text_width(text width)" +[//]: # " end" +[//]: # " subgraph state" +[//]: # " direction TB" +[//]: # " subgraph div state" +[//]: # " direction TB" +[//]: # " state1(state)-->color1(color)" +[//]: # " state1-->border1(border)" +[//]: # " text_width-.->layout_width1(layout width)" +[//]: # " linkStyle 2 stroke:#ff5500,stroke-width:4px;" +[//]: # " state1-->layout_width1" +[//]: # " end" +[//]: # " subgraph p state" +[//]: # " direction TB" +[//]: # " state2(state)-->color2(color)" +[//]: # " color1-.->color2" +[//]: # " linkStyle 5 stroke:#0000ff,stroke-width:4px;" +[//]: # " state2-->border2(border)" +[//]: # " text_width-.->layout_width2(layout width)" +[//]: # " linkStyle 7 stroke:#ff5500,stroke-width:4px;" +[//]: # " state2-->layout_width2" +[//]: # " layout_width2-.->layout_width1" +[//]: # " linkStyle 9 stroke:#00aa00,stroke-width:4px;" +[//]: # " end" +[//]: # " subgraph hello world state" +[//]: # " direction TB" +[//]: # " state3(state)-->border3(border)" +[//]: # " state3-->color3(color)" +[//]: # " color2-.->color3" +[//]: # " linkStyle 12 stroke:#0000ff,stroke-width:4px;" +[//]: # " text_width-.->layout_width3(layout width)" +[//]: # " linkStyle 13 stroke:#ff5500,stroke-width:4px;" +[//]: # " state3-->layout_width3" +[//]: # " layout_width3-.->layout_width2" +[//]: # " linkStyle 15 stroke:#00aa00,stroke-width:4px;" +[//]: # " end" +[//]: # " end" Para ajudar na construção de um DOM, o núcleo nativo fornece quatro `traits`: `State`, `ChildDepState`, `ParentDepState` e `NodeDepState` e uma estrutura `RealDom`. O `ChildDepState`, `ParentDepState` e `NodeDepState` fornecem uma maneira de descrever como algumas informações em um nó se relacionam com as de seus parentes. Ao fornecer como construir um único nó a partir de suas relações, o native-core derivará uma maneira de atualizar o estado de todos os nós para você com `#[derive(State)]`. Depois de ter um estado, você pode fornecê-lo como genérico ao `RealDom`. `RealDom` fornece todos os métodos para interagir e atualizar seu novo dom. -```rust +```rust, no_run use dioxus_native_core::node_ref::*; use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; use dioxus_native_core_macro::{sorted_str_slice, State}; @@ -455,7 +455,7 @@ struct ToyState { Agora que temos nosso estado, podemos colocá-lo em uso em nosso DOM. Você pode atualizar o DOM com `update_state` para atualizar a estrutura do dom (adicionando, removendo e alterando as propriedades dos nós) e então `apply_mutations` para atualizar o `ToyState` para cada um dos nós que foram alterados. -```rust +```rust, no_run fn main(){ fn app(cx: Scope) -> Element { cx.render(rsx!{ diff --git a/docs/guide/src/pt-br/describing_ui/component_children.md b/docs/guide/src/pt-br/describing_ui/component_children.md index 433213616..9663dca19 100644 --- a/docs/guide/src/pt-br/describing_ui/component_children.md +++ b/docs/guide/src/pt-br/describing_ui/component_children.md @@ -2,13 +2,13 @@ Em alguns casos, você pode desejar criar um componente que atue como um contêiner para algum outro conteúdo, sem que o componente precise saber qual é esse conteúdo. Para conseguir isso, crie uma _prop_ do tipo `Element`: -```rust +```rust, no_run {{#include ../../../examples/component_element_props.rs:Clickable}} ``` Então, ao renderizar o componente, você pode passar a saída de `cx.render(rsx!(...))`: -```rust +```rust, no_run {{#include ../../../examples/component_element_props.rs:Clickable_usage}} ``` @@ -20,12 +20,12 @@ Então, ao renderizar o componente, você pode passar a saída de `cx.render(rsx Em vez de passar o `RSX` através de uma _prop_ regular, você pode querer aceitar filhos da mesma forma que os elementos podem ter filhos. O prop "mágico" `children` permite que você consiga isso: -```rust +```rust, no_run {{#include ../../../examples/component_children.rs:Clickable}} ``` Isso torna o uso do componente muito mais simples: basta colocar o `RSX` dentro dos colchetes `{}` – e não há necessidade de uma chamada `render` ou outra macro! -```rust +```rust, no_run {{#include ../../../examples/component_children.rs:Clickable_usage}} ``` diff --git a/docs/guide/src/pt-br/describing_ui/component_props.md b/docs/guide/src/pt-br/describing_ui/component_props.md index 7d3b8e976..4cb5a50ca 100644 --- a/docs/guide/src/pt-br/describing_ui/component_props.md +++ b/docs/guide/src/pt-br/describing_ui/component_props.md @@ -19,13 +19,13 @@ Existem 2 tipos de estruturas Props: _Props_ próprios são muito simples – eles não emprestam nada. Exemplo: -```rust +```rust, no_run {{#include ../../../examples/component_owned_props.rs:Likes}} ``` Você pode então passar valores de _prop_ para o componente da mesma forma que você passaria atributos para um elemento: -```rust +```rust, no_run {{#include ../../../examples/component_owned_props.rs:App}} ``` @@ -37,13 +37,13 @@ Possuir _props_ funciona bem se seus _props_ forem fáceis de copiar – como um Rust permite algo mais eficiente – emprestar a `String` como um `&str` – é para isso que servem as _props emprestadas_! -```rust +```rust, no_run {{#include ../../../examples/component_borrowed_props.rs:TitleCard}} ``` Podemos então usar o componente assim: -```rust +```rust, no_run {{#include ../../../examples/component_borrowed_props.rs:App}} ``` @@ -57,13 +57,13 @@ A macro `#[derive(Props)]` tem alguns recursos que permitem personalizar o compo Você pode criar campos opcionais usando o tipo `Option<…>` para um campo: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:OptionalProps}} ``` Em seguida, você pode optar por fornecê-los ou não: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:OptionalProps_usage}} ``` @@ -71,13 +71,13 @@ Em seguida, você pode optar por fornecê-los ou não: Se você quiser exigir explicitamente uma `Option`, e não uma _prop_ opcional, você pode anotá-la com `#[props(!optional)]`: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:ExplicitOption}} ``` Então, você tem que passar explicitamente `Some("str")` ou `None`: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:ExplicitOption_usage}} ``` @@ -85,13 +85,13 @@ Então, você tem que passar explicitamente `Some("str")` ou `None`: Você pode usar `#[props(default = 42)]` para tornar um campo opcional e especificar seu valor padrão: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:DefaultComponent}} ``` Então, da mesma forma que _props_ opcionais, você não precisa fornecê-lo: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:DefaultComponent_usage}} ``` @@ -99,13 +99,13 @@ Então, da mesma forma que _props_ opcionais, você não precisa fornecê-lo: É comum que as funções Rust aceitem `impl Into` em vez de apenas `SomeType` para suportar uma ampla gama de parâmetros. Se você quiser uma funcionalidade semelhante com _props_, você pode usar `#[props(into)]`. Por exemplo, você pode adicioná-lo em uma prop `String` – e `&str` também será aceito automaticamente, pois pode ser convertido em `String`: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:IntoComponent}} ``` Então, você pode usá-lo assim: -```rust +```rust, no_run {{#include ../../../examples/component_props_options.rs:IntoComponent_usage}} ``` @@ -115,7 +115,7 @@ Até agora, todas as funções `Component` que vimos tinham uma _struct_ `Compon `inline_props` permite que você faça exatamente isso. Em vez de digitar a versão "completa": -```rust +```rust, no_run #[derive(Props, PartialEq)] struct TitleCardProps { title: String, @@ -130,7 +130,7 @@ fn TitleCard(cx: Scope) -> Element { ...você pode definir uma função que aceita _props_ como argumentos. Então, basta anotá-lo com `#[inline_props]`, e a macro irá transformá-lo em um componente regular para você: -```rust +```rust, no_run #[inline_props] fn TitleCard(cx: Scope, title: String) -> Element { cx.render(rsx!{ diff --git a/docs/guide/src/pt-br/describing_ui/components.md b/docs/guide/src/pt-br/describing_ui/components.md index d33585cea..91123b5f8 100644 --- a/docs/guide/src/pt-br/describing_ui/components.md +++ b/docs/guide/src/pt-br/describing_ui/components.md @@ -4,7 +4,7 @@ Assim como você não gostaria de escrever um programa complexo em uma única e Um componente é uma função Rust, nomeada em _UpperCammelCase_, que recebe um parâmetro `Scope` e retorna um `Element` descrevendo a interface do usuário que deseja renderizar. Na verdade, nossa função `App` é um componente! -```rust +```rust, no_run {{#include ../../../examples/hello_world_desktop.rs:component}} ``` @@ -12,13 +12,13 @@ Um componente é uma função Rust, nomeada em _UpperCammelCase_, que recebe um Um Componente é responsável por alguma tarefa de renderização – normalmente, renderizando uma parte isolada da interface do usuário. Por exemplo, você pode ter um componente `About` que renderiza uma breve descrição do Dioxus Labs: -```rust +```rust, no_run {{#include ../../../examples/components.rs:About}} ``` Em seguida, você pode renderizar seu componente em outro componente, da mesma forma que os elementos são renderizados: -```rust +```rust, no_run {{#include ../../../examples/components.rs:App}} ``` diff --git a/docs/guide/src/pt-br/describing_ui/index.md b/docs/guide/src/pt-br/describing_ui/index.md index 8da134903..212e67921 100644 --- a/docs/guide/src/pt-br/describing_ui/index.md +++ b/docs/guide/src/pt-br/describing_ui/index.md @@ -4,7 +4,7 @@ Dioxus é uma estrutura _declarativa_. Isso significa que, em vez de dizer ao Di Você já viu um exemplo simples ou sintaxe `RSX` no aplicativo "hello world": -```rust +```rust, no_run {{#include ../../../examples/hello_world_desktop.rs:component}} ``` @@ -14,7 +14,7 @@ Aqui, usamos a macro `rsx!` para _declarar_ que queremos um elemento `div`, cont O RSX é muito semelhante ao HTML, pois descreve elementos com atributos e filhos. Aqui está um elemento `div` vazio no RSX, bem como o HTML resultante: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:empty}} ``` @@ -26,7 +26,7 @@ O RSX é muito semelhante ao HTML, pois descreve elementos com atributos e filho Para adicionar filhos a um elemento, coloque-os dentro dos colchetes `{}`. Eles podem ser outros elementos ou texto. Por exemplo, você pode ter um elemento `ol` (lista ordenada), contendo 3 elementos `li` (item da lista), cada um dos quais contém algum texto: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:children}} ``` @@ -44,7 +44,7 @@ Você também pode "agrupar" elementos envolvendo-os em `Fragment {}`. Isso não > Nota: você também pode renderizar vários elementos no nível superior de `rsx!` e eles serão agrupados automaticamente – não há necessidade de um `Fragment {}` explícito lá. -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:fragments}} ``` @@ -60,7 +60,7 @@ Você também pode "agrupar" elementos envolvendo-os em `Fragment {}`. Isso não Os atributos também são especificados dentro dos colchetes `{}`, usando a sintaxe `name: value`. Você pode fornecer o valor como um literal no RSX: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:attributes}} ``` @@ -79,7 +79,7 @@ Os atributos também são especificados dentro dos colchetes `{}`, usando a sint Dioxus tem um conjunto pré-configurado de atributos que você pode usar. O RSX é validado em tempo de compilação para garantir que você não especificou um atributo inválido. Se você quiser substituir esse comportamento por um nome de atributo personalizado, especifique o atributo entre aspas: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:custom_attributes}} ``` @@ -91,7 +91,7 @@ Dioxus tem um conjunto pré-configurado de atributos que você pode usar. O RSX Da mesma forma que você pode [formatar](https://doc.rust-lang.org/rust-by-example/hello/print/fmt.html) Rust _strings_, você também pode interpolar no texto RSX. Use `{variable}` para exibir o valor de uma variável em uma _string_, ou `{variable:?}` para usar a representação `Debug`: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:formatting}} ``` @@ -107,7 +107,7 @@ Da mesma forma que você pode [formatar](https://doc.rust-lang.org/rust-by-examp Você pode incluir expressões Rust arbitrárias dentro do RSX, mas deve escapá-las entre colchetes `[]`: -```rust +```rust, no_run {{#include ../../../examples/rsx_overview.rs:expression}} ``` diff --git a/docs/guide/src/pt-br/describing_ui/special_attributes.md b/docs/guide/src/pt-br/describing_ui/special_attributes.md index e196f667b..01bfe9b25 100644 --- a/docs/guide/src/pt-br/describing_ui/special_attributes.md +++ b/docs/guide/src/pt-br/describing_ui/special_attributes.md @@ -8,7 +8,7 @@ Se você estiver trabalhando com itens pré-renderizados, modelos ou uma bibliot Por exemplo, enviar um conversor de markdown para Dioxus pode aumentar significativamente o tamanho final do aplicativo. Em vez disso, você desejará pré-renderizar sua remarcação para HTML e, em seguida, incluir o HTML diretamente em sua saída. Usamos essa abordagem para a [página inicial do Dioxus](https://dioxuslabs.com): -```rust +```rust, no_run {{#include ../../../examples/dangerous_inner_html.rs:dangerous_inner_html}} ``` @@ -22,7 +22,7 @@ A maioria dos atributos, quando renderizados, serão renderizados exatamente com Portanto, este RSX não renderizaria o atributo `hidden`: -```rust +```rust, no_run {{#include ../../../examples/boolean_attribute.rs:boolean_attribute}} ``` diff --git a/docs/guide/src/pt-br/getting_started/desktop.md b/docs/guide/src/pt-br/getting_started/desktop.md index 5c6940b96..70f71aa1a 100644 --- a/docs/guide/src/pt-br/getting_started/desktop.md +++ b/docs/guide/src/pt-br/getting_started/desktop.md @@ -35,6 +35,6 @@ cargo add dioxus-desktop Edite seu `main.rs`: -```rust +```rust, no_run {{#include ../../../examples/hello_world_desktop.rs:all}} ``` diff --git a/docs/guide/src/pt-br/getting_started/mobile.md b/docs/guide/src/pt-br/getting_started/mobile.md index edcfbfba7..66a17272b 100644 --- a/docs/guide/src/pt-br/getting_started/mobile.md +++ b/docs/guide/src/pt-br/getting_started/mobile.md @@ -59,7 +59,7 @@ simple_logger = "*" Edite seu `lib.rs`: -```rust +```rust, no_run use dioxus::prelude::*; fn main() { diff --git a/docs/guide/src/pt-br/getting_started/ssr.md b/docs/guide/src/pt-br/getting_started/ssr.md index ddb02a289..ebf478163 100644 --- a/docs/guide/src/pt-br/getting_started/ssr.md +++ b/docs/guide/src/pt-br/getting_started/ssr.md @@ -16,7 +16,7 @@ Ao trabalhar com frameworks web que requerem `Send`, é possível renderizar um Se você quer apenas renderizar `rsx!` ou um VirtualDom para HTML, confira os documentos da API. É bem simples: -```rust +```rust, no_run // We can render VirtualDoms let mut vdom = VirtualDom::new(app); let _ = vdom.rebuild(); @@ -61,7 +61,7 @@ tokio = { version = "1.15.0", features = ["full"] } Agora, configure seu aplicativo Axum para responder em um _endpoint_. -```rust +```rust, no_run use axum::{response::Html, routing::get, Router}; use dioxus::prelude::*; @@ -83,7 +83,7 @@ async fn main() { E, em seguida, adicione nosso _endpoint_. Podemos renderizar `rsx!` diretamente: -```rust +```rust, no_run async fn app_endpoint() -> Html { Html(dioxus_ssr::render_lazy(rsx! { h1 { "hello world!" } @@ -93,7 +93,7 @@ async fn app_endpoint() -> Html { Ou podemos renderizar `VirtualDoms`. -```rust +```rust, no_run async fn app_endpoint() -> Html { fn app(cx: Scope) -> Element { cx.render(rsx!(h1 { "hello world" })) diff --git a/docs/guide/src/pt-br/getting_started/tui.md b/docs/guide/src/pt-br/getting_started/tui.md index deaf060b6..ba4e2ecda 100644 --- a/docs/guide/src/pt-br/getting_started/tui.md +++ b/docs/guide/src/pt-br/getting_started/tui.md @@ -23,7 +23,7 @@ cargo add dioxus-tui Em seguida, edite seu `main.rs` com o modelo básico. -```rust +```rust, no_run {{#include ../../../examples/hello_world_tui.rs}} ``` @@ -35,7 +35,7 @@ cargo run Pressione "ctrl-c" para fechar o aplicativo. Para mudar de "ctrl-c" para apenas "q" para sair, você pode iniciar o aplicativo com uma configuração para desativar o sair padrão e usar a raiz TuiContext para sair por conta própria. -```rust +```rust, no_run {{#include ../../../examples/hello_world_tui_no_ctrl_c.rs}} ``` diff --git a/docs/guide/src/pt-br/getting_started/web.md b/docs/guide/src/pt-br/getting_started/web.md index ffb322a25..47991b595 100644 --- a/docs/guide/src/pt-br/getting_started/web.md +++ b/docs/guide/src/pt-br/getting_started/web.md @@ -64,7 +64,7 @@ Adicione um `index.html` para o `Trunk` usar. Certifique-se de que seu elemento Edite seu `main.rs`: -```rust +```rust, no_run {{#include ../../../examples/hello_world_web.rs}} ``` diff --git a/docs/guide/src/pt-br/index.md b/docs/guide/src/pt-br/index.md index 121f3ef45..e3d32ebc2 100644 --- a/docs/guide/src/pt-br/index.md +++ b/docs/guide/src/pt-br/index.md @@ -4,7 +4,7 @@ Dioxus é uma estrutura portátil, de alto desempenho e ergonômica para a construção de interfaces de usuário multiplataforma no Rust. Este guia irá ajudá-lo a começar a escrever aplicativos Dioxus para a Web, Desktop, Mobile e muito mais. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let mut count = use_state(cx, || 0); diff --git a/docs/guide/src/pt-br/interactivity/custom_hooks.md b/docs/guide/src/pt-br/interactivity/custom_hooks.md index 881a0946a..c2ac570c4 100644 --- a/docs/guide/src/pt-br/interactivity/custom_hooks.md +++ b/docs/guide/src/pt-br/interactivity/custom_hooks.md @@ -8,7 +8,7 @@ Para evitar a repetição, você pode encapsular a lógica de negócios com base Por exemplo, se muitos componentes precisam acessar uma _struct_ `AppSettings`, você pode criar um gancho de "atalho": -```rust +```rust, no_run {{#include ../../../examples/hooks_composed.rs:wrap_context}} ``` diff --git a/docs/guide/src/pt-br/interactivity/dynamic_rendering.md b/docs/guide/src/pt-br/interactivity/dynamic_rendering.md index 2298735e5..58c120926 100644 --- a/docs/guide/src/pt-br/interactivity/dynamic_rendering.md +++ b/docs/guide/src/pt-br/interactivity/dynamic_rendering.md @@ -6,7 +6,7 @@ Para renderizar diferentes elementos com base em uma condição, você pode usar uma instrução `if-else`: -```rust +```rust, no_run {{#include ../../../examples/conditional_rendering.rs:if_else}} ``` @@ -16,7 +16,7 @@ Para renderizar diferentes elementos com base em uma condição, você pode usar Como `Element` é uma `Option`, os componentes que aceitam `Element` como _prop_ podem realmente inspecionar seu conteúdo e renderizar coisas diferentes com base nisso. Exemplo: -```rust +```rust, no_run {{#include ../../../examples/component_children_inspect.rs:Clickable}} ``` @@ -26,7 +26,7 @@ Você não pode modificar o `Element`, mas se precisar de uma versão modificada Para renderizar nada, você pode retornar `None` de um componente. Isso é útil se você deseja ocultar algo condicionalmente: -```rust +```rust, no_run {{#include ../../../examples/conditional_rendering.rs:conditional_none}} ``` @@ -47,7 +47,7 @@ Para isso, o Dioxus aceita iteradores que produzem `Element`s. Então precisamos Exemplo: suponha que você tenha uma lista de comentários que deseja renderizar. Então, você pode renderizá-los assim: -```rust +```rust, no_run {{#include ../../../examples/rendering_lists.rs:render_list}} ``` diff --git a/docs/guide/src/pt-br/interactivity/event_handlers.md b/docs/guide/src/pt-br/interactivity/event_handlers.md index 2143dba4a..12aad321e 100644 --- a/docs/guide/src/pt-br/interactivity/event_handlers.md +++ b/docs/guide/src/pt-br/interactivity/event_handlers.md @@ -8,7 +8,7 @@ Os manipuladores de eventos são semelhantes aos atributos regulares, mas seus n Por exemplo, para manipular cliques em um elemento, podemos especificar um manipulador `onclick`: -```rust +```rust, no_run {{#include ../../../examples/event_click.rs:rsx}} ``` @@ -29,7 +29,7 @@ Para saber o que os diferentes tipos de eventos fornecem, leia os [documentos do Quando você tem, por exemplo um `button` dentro de um `div`, qualquer clique no `button` também é um clique no `div`. Por esta razão, Dioxus propaga o evento click: primeiro, ele é acionado no elemento alvo, depois nos elementos pai. Se você quiser evitar esse comportamento, você pode chamar `cancel_bubble()` no evento: -```rust +```rust, no_run {{#include ../../../examples/event_click.rs:rsx}} ``` @@ -39,7 +39,7 @@ Alguns eventos têm um comportamento padrão. Para eventos de teclado, isso pode Em alguns casos, você pode querer evitar esse comportamento padrão. Para isso, você pode adicionar o atributo `prevent_default` com o nome do manipulador cujo comportamento padrão você deseja interromper. Este atributo é especial: você pode anexá-lo várias vezes para vários atributos: -```rust +```rust, no_run {{#include ../../../examples/event_prevent_default.rs:prevent_default}} ``` @@ -51,13 +51,13 @@ Quaisquer manipuladores de eventos ainda serão chamados. Às vezes, você pode querer criar um componente que aceite um manipulador de eventos. Um exemplo simples seria um componente `FancyButton`, que aceita um manipulador `on_click`: -```rust +```rust, no_run {{#include ../../../examples/event_handler_prop.rs:component_with_handler}} ``` Então, você pode usá-lo como qualquer outro manipulador: -```rust +```rust, no_run {{#include ../../../examples/event_handler_prop.rs:usage}} ``` diff --git a/docs/guide/src/pt-br/interactivity/hooks.md b/docs/guide/src/pt-br/interactivity/hooks.md index 5f918f623..c5dd85d41 100644 --- a/docs/guide/src/pt-br/interactivity/hooks.md +++ b/docs/guide/src/pt-br/interactivity/hooks.md @@ -14,7 +14,7 @@ Para lógica com estado, você pode usar _hooks_. _Hooks_ são funções Rust qu Por exemplo, você pode ter visto o exemplo do contador, no qual o estado (um número) é rastreado usando o _hook_ `use_state`: -```rust +```rust, no_run {{#include ../../../examples/hooks_counter.rs:component}} ``` @@ -26,7 +26,7 @@ Toda vez que o estado do componente muda, ele é renderizado novamente e a funç Você pode usar vários _hooks_ no mesmo componente se quiser: -```rust +```rust, no_run {{#include ../../../examples/hooks_counter_two_state.rs:component}} ``` @@ -38,7 +38,7 @@ O exemplo acima pode parecer um pouco mágico, já que as funções Rust normalm Mas como Dioxus pode diferenciar entre vários _hooks_ no mesmo componente? Como você viu no segundo exemplo, ambas as funções `use_state` foram chamadas com os mesmos parâmetros, então como elas podem retornar coisas diferentes quando os contadores são diferentes? -```rust +```rust, no_run {{#include ../../../examples/hooks_counter_two_state.rs:use_state_calls}} ``` @@ -54,19 +54,19 @@ Essas regras significam que há certas coisas que você não pode fazer com _hoo ### Sem Hooks em Condicionais -```rust +```rust, no_run {{#include ../../../examples/hooks_bad.rs:conditional}} ``` ### Sem Hooks em Closures -```rust +```rust, no_run {{#include ../../../examples/hooks_bad.rs:closure}} ``` ### Sem Hooks em Loops -```rust +```rust, no_run {{#include ../../../examples/hooks_bad.rs:loop}} ``` @@ -80,7 +80,7 @@ Felizmente, existe outro _hook_ para isso, `use_ref`! É semelhante ao `use_stat Aqui está um exemplo simples que mantém uma lista de eventos em um `use_ref`. Podemos adquirir acesso de escrita ao estado com `.write()`, e então apenas `.push` um novo valor para o estado: -```rust +```rust, no_run {{#include ../../../examples/hooks_use_ref.rs:component}} ``` diff --git a/docs/guide/src/pt-br/interactivity/router.md b/docs/guide/src/pt-br/interactivity/router.md index 382befddb..398546c0c 100644 --- a/docs/guide/src/pt-br/interactivity/router.md +++ b/docs/guide/src/pt-br/interactivity/router.md @@ -26,7 +26,7 @@ dioxus-router = { version = "*" } Ao contrário de outros roteadores no ecossistema Rust, nosso roteador é construído de forma declarativa. Isso torna possível compor o layout do nosso aplicativo simplesmente organizando os componentes. -```rust +```rust, no_run rsx!{ Router { Route { to: "/home", Home {} } @@ -41,7 +41,7 @@ Podemos corrigir isso de duas maneiras: - Uma página 404 de _fallback_ -```rust +```rust, no_run rsx!{ Router { Route { to: "/home", Home {} } @@ -53,7 +53,7 @@ rsx!{ - Redirecionar 404 para _Home_ -```rust +```rust, no_run rsx!{ Router { Route { to: "/home", Home {} } @@ -67,7 +67,7 @@ rsx!{ Para que nosso aplicativo navegue nessas rotas, podemos fornecer elementos clicáveis chamados Links. Eles simplesmente envolvem elementos `` que, quando clicados, navegam no aplicativo para o local determinado. -```rust +```rust, no_run rsx!{ Link { to: "/home", diff --git a/docs/guide/src/pt-br/interactivity/sharing_state.md b/docs/guide/src/pt-br/interactivity/sharing_state.md index dba11f17c..b9b829c0c 100644 --- a/docs/guide/src/pt-br/interactivity/sharing_state.md +++ b/docs/guide/src/pt-br/interactivity/sharing_state.md @@ -12,7 +12,7 @@ Por exemplo, suponha que queremos construir um editor de memes. Queremos ter uma Começamos com um componente `Meme`, responsável por renderizar um meme com uma determinada legenda: -```rust +```rust, no_run {{#include ../../../examples/meme_editor.rs:meme_component}} ``` @@ -20,13 +20,13 @@ Começamos com um componente `Meme`, responsável por renderizar um meme com uma Também criamos um editor de legendas, totalmente desacoplado do meme. O editor de legendas não deve armazenar a legenda em si – caso contrário, como iremos fornecê-la ao componente `Meme`? Em vez disso, ele deve aceitar a legenda atual como um suporte, bem como um manipulador de eventos para delegar eventos de entrada para: -```rust +```rust, no_run {{#include ../../../examples/meme_editor.rs:caption_editor}} ``` Finalmente, um terceiro componente renderizará os outros dois como filhos. Ele será responsável por manter o estado e passar os _props_ relevantes. -```rust +```rust, no_run {{#include ../../../examples/meme_editor.rs:meme_editor}} ``` @@ -46,19 +46,19 @@ A Dioxus oferece uma solução melhor do que esta "perfuração com hélice" – Primeiro, temos que criar um _struct_ para nossa configuração de modo escuro: -```rust +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:DarkMode_struct}} ``` Agora, em um componente de nível superior (como `App`), podemos fornecer o contexto `DarkMode` para todos os componentes filhos: -```rust +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:context_provider}} ``` Como resultado, qualquer componente filho de `App` (direto ou não), pode acessar o contexto `DarkMode`. -```rust +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:use_context}} ``` @@ -66,6 +66,6 @@ Como resultado, qualquer componente filho de `App` (direto ou não), pode acessa Por exemplo, aqui está como implementaríamos a alternância do modo escuro, que lê o contexto (para determinar a cor que deve renderizar) e grava nele (para alternar o modo escuro): -```rust +```rust, no_run {{#include ../../../examples/meme_editor_dark_mode.rs:toggle}} ``` diff --git a/docs/guide/src/pt-br/interactivity/user_input.md b/docs/guide/src/pt-br/interactivity/user_input.md index 1afbce439..94a091e38 100644 --- a/docs/guide/src/pt-br/interactivity/user_input.md +++ b/docs/guide/src/pt-br/interactivity/user_input.md @@ -6,7 +6,7 @@ As interfaces geralmente precisam fornecer uma maneira de inserir dados: por exe Com entradas controladas, você fica diretamente responsável pelo estado da entrada. Isso lhe dá muita flexibilidade e facilita manter as coisas em sincronia. Por exemplo, é assim que você criaria uma entrada de texto controlada: -```rust +```rust, no_run {{#include ../../../examples/input_controlled.rs:component}} ``` @@ -24,7 +24,7 @@ Como alternativa às entradas controladas, você pode simplesmente deixar a plat Como você não tem necessariamente o valor atual da entrada não controlada no estado, você pode acessá-lo ouvindo os eventos `oninput` (de maneira semelhante aos componentes controlados) ou, se a entrada for parte de um formulário, você pode acessar os dados do formulário nos eventos do formulário (por exemplo, `oninput` ou `onsubmit`): -```rust +```rust, no_run {{#include ../../../examples/input_uncontrolled.rs:component}} ``` diff --git a/docs/posts/release-0-2-0.md b/docs/posts/release-0-2-0.md index 7aed2fda6..7901ee8b0 100644 --- a/docs/posts/release-0-2-0.md +++ b/docs/posts/release-0-2-0.md @@ -20,12 +20,11 @@ Thanks to these amazing folks for their code contributions: - [@oovm](https://github.com/oovm) - [@6asaaki](https://github.com/6asaaki) - Just over two months in, and we already a ton of awesome changes to Dioxus! Dioxus is a recently-released library for building interactive user interfaces (GUI) with Rust. It is built around a Virtual DOM, making it portable for the web, desktop, server, mobile, and more. Dioxus looks and feels just like React, so if you know React, then you'll feel right at home. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let mut count = use_state(cx, || 0); @@ -39,7 +38,7 @@ fn app(cx: Scope) -> Element { # What's new? -A *ton* of stuff happened in this release; 550+ commits, 23 contributors, 2 minor releases, and 6 backers on Open Collective. +A _ton_ of stuff happened in this release; 550+ commits, 23 contributors, 2 minor releases, and 6 backers on Open Collective. Some of the major new features include: @@ -52,12 +51,10 @@ Some of the major new features include: We also fixed and improved a bunch of stuff - check out the full list down below. - ## A New Renderer: Your terminal! When Dioxus was initially released, we had very simple support for logging Dioxus elements out as TUI elements. In the past month or so, [@Demonthos](https://github.com/Demonthos) really stepped up and made the new crate a reality. - ![Imgur](https://i.imgur.com/GL7uu3r.png) The new TUI renderer even supports mouse movements, keyboard input, async tasks, borders, and a ton more. @@ -66,15 +63,13 @@ The new TUI renderer even supports mouse movements, keyboard input, async tasks, - - ## New Router We totally revamped the router, switching away from the old yew-router approach to the more familiar [React-Router](http://reactrouter.com). It's less type-safe but provides more flexibility and support for beautiful URLs. -Apps with routers are *really* simple now. It's easy to compose the "Router", a "Route", and "Links" to define how your app is laid out: +Apps with routers are _really_ simple now. It's easy to compose the "Router", a "Route", and "Links" to define how your app is laid out: -```rust +```rust, no_run fn app(cx: Scope) -> Element { cx.render(rsx! { Router { @@ -97,7 +92,7 @@ fn app(cx: Scope) -> Element { We're also using hooks to parse the URL parameters and segments so you can interact with the router from anywhere deeply nested in your app. -```rust +```rust, no_run #[derive(Deserialize)] struct Query { name: String } @@ -122,7 +117,7 @@ Managing state in your app can be challenging. Building global state management Fermi uses the concept of "Atoms" for global state. These individual values can be get/set from anywhere in your app. Using state with Fermi is basically as simple as `use_state`. -```rust +```rust, no_run // Create a single value in an "Atom" static TITLE: Atom<&str> = |_| "Hello"; @@ -152,7 +147,8 @@ fn Child(cx: Scope) -> Element { For internal components, explicitly declaring props structs can become tedious. That's why we've built the new `inline_props` macro. This macro lets you inline your props definition right into your component function arguments. Simply add the `inline_props` macro to your component: -```rust + +```rust, no_run #[inline_props] fn Child<'a>( cx: Scope, @@ -174,9 +170,9 @@ You won't be able to document each field or attach attributes so you should refr ## Props optional fields -Sometimes you don't want to specify *every* value in a component's props, since there might a lot. That's why the `Props` macro now supports optional fields. You can use a combination of `default`, `strip_option`, and `optional` to tune the exact behavior of properties fields. +Sometimes you don't want to specify _every_ value in a component's props, since there might a lot. That's why the `Props` macro now supports optional fields. You can use a combination of `default`, `strip_option`, and `optional` to tune the exact behavior of properties fields. -```rust +```rust, no_run #[derive(Props, PartialEq)] struct ChildProps { #[props(default = "client")] @@ -207,16 +203,15 @@ Under the hood, we have a new string interning engine to cache commonly used tag Overall, Dioxus apps are even more snappy than before. - Before and after: ![Before and After](https://imgur.com/byTBGlO.png) - ## Dioxus Desktop Window Context A very welcome change, thanks AGAIN to [@mrxiaozhuox](https://github.com/mrxiaozhuox) is support for imperatively controlling the desktop window from your Dioxus code. A bunch of new methods were added: + - Minimize and maximize window - Close window - Focus window @@ -240,10 +235,9 @@ Unlike its counterpart, `Trunk.rs`, the dioxus-cli supports running examples and Working with async isn't the easiest part of Rust. To help improve things, we've upgraded async support across the board in Dioxus. - First, we upgraded the `use_future` hook. It now supports dependencies, which let you regenerate a future on the fly as its computed values change. It's never been easier to add datafetching to your Rust Web Apps: -```rust +```rust, no_run fn RenderDog(cx: Scope, breed: String) -> Element { let dog_request = use_future(cx, (breed,), |(breed,)| async move { reqwest::get(format!("https://dog.ceo/api/breed/{}/images/random", breed)) @@ -263,7 +257,7 @@ fn RenderDog(cx: Scope, breed: String) -> Element { Additionally, we added better support for coroutines. You can now start, stop, resume, and message with asynchronous tasks. The coroutine is automatically exposed to the rest of your app via the Context API. For the vast majority of apps, Coroutines can satisfy all of your state management needs: -```rust +```rust, no_run fn App(cx: Scope) -> Element { let sync_task = use_coroutine(cx, |rx| async move { connect_to_server().await; @@ -313,6 +307,7 @@ We've covered the major headlining features, but there were so many more! - Right-click menus are now disabled by default ## Fixes + - Windows support improved across the board - Linux support improved across the board - Bug in Calculator example @@ -329,6 +324,7 @@ A ton more! Dioxus is now much more stable than it was at release! - The ContextAPI no longer uses RC to share state - anything that is `Clone` can be shared ## Community Additions + - [Styled Components macro](https://github.com/Zomatree/Revolt-Client/blob/master/src/utils.rs#14-27) [@Zomatree](https://github.com/Zomatree) - [Dioxus-Websocket hook](https://github.com/FruitieX/dioxus-websocket-hooks) [@FruitieX](https://github.com/FruitieX) - [Home automation server app](https://github.com/FruitieX/homectl) [@FruitieX](https://github.com/FruitieX) @@ -363,5 +359,3 @@ If you're interested in building an app with Dioxus, make sure to check us out o - [Reddit](http://reddit.com/r/dioxus/) - [Discord](https://discord.gg/XgGxMSkvUM) - [Twitter](http://twitter.com/dioxuslabs) - - diff --git a/docs/posts/release.md b/docs/posts/release.md index 171664dd3..06aca2ef5 100644 --- a/docs/posts/release.md +++ b/docs/posts/release.md @@ -20,7 +20,7 @@ Dioxus is designed to be familiar for developers already comfortable with React To give you an idea of what Dioxus looks like, here's a simple counter app: -```rust +```rust, no_run use dioxus::prelude::*; fn main() { @@ -112,8 +112,8 @@ Semantically, TypeScript-React and Rust-Dioxus are very similar. In TypeScript, ```tsx type CardProps = { - title: string, - paragraph: string, + title: string; + paragraph: string; }; const Card: FunctionComponent = (props) => { @@ -122,7 +122,7 @@ const Card: FunctionComponent = (props) => { ); }; @@ -130,7 +130,7 @@ const Card: FunctionComponent = (props) => { In Dioxus, we would define the same component in a similar fashion: -```rust +```rust, no_run #[derive(Props, PartialEq)] struct CardProps { title: String, @@ -176,14 +176,14 @@ $ cd dioxus_example We then add a dependency on Dioxus to the `Cargo.toml` file, with the "desktop" feature enabled: -```rust +```rust, no_run [dependencies] dioxus = { version = "*", features = ["desktop"] } ``` We can add our counter from above. -```rust +```rust, no_run use dioxus::prelude::*; fn main() { @@ -203,14 +203,13 @@ fn app(cx: Scope) -> Element { And voilà! We can `cargo run` our app - ![Simple Counter Desktop App](/static/counter.png) ## Support for JSX-style templating Dioxus ships with a templating macro called RSX, a spin on React's JSX. RSX is very similar to regular struct syntax for Rust so it integrates well with your IDE. If used with [Rust-Analyzer](https://github.com/rust-analyzer/rust-analyzer) (not tested anywhere else) RSX supports code-folding, block selection, bracket pair colorizing, autocompletion, symbol renaming — pretty much anything you would expect from writing regular struct-style code. -```rust +```rust, no_run rsx! { div { "Hello world" } button { @@ -222,7 +221,7 @@ rsx! { If macros aren't your style, you can always drop down to the factory API: -```rust +```rust, no_run LazyNodes::new(|f| { f.fragment([ f.element(div, [f.text("hello world")], [], None, None) @@ -245,9 +244,9 @@ To make it easier to work with RSX, we've built a small [VSCode extension](https Many of the Rust UI frameworks are particularly difficult to work with. Even the ones branded as "ergonomic" are quite challenging to in comparison to TSX/JSX. With Dioxus, we've innovated on a number of Rust patterns to deliver a framework that is actually enjoyable to develop in. -For example, many Rust frameworks require you to clone your data in for *every* closure and handler you use. This can get really clumsy for large apps. +For example, many Rust frameworks require you to clone your data in for _every_ closure and handler you use. This can get really clumsy for large apps. -```rust +```rust, no_run div() .children([ button().onclick(cloned!(name, date, age, description => move |evt| { /* */ }) @@ -258,8 +257,7 @@ div() Dioxus understands the lifetimes of data borrowed from `Scope`, so you can safely return any borrowed data without declaring explicit captures. Hook handles all implement `Copy` so they can be shared between listeners without any ceremony. - -```rust +```rust, no_run let name = use_state(cx, || "asd"); rsx! { div { @@ -272,7 +270,7 @@ rsx! { Because we know the lifetime of your handlers, we can also expose this to children. No other Rust frameworks let us share borrowed state through the tree, forcing use of Rc/Arc everywhere. With Dioxus, all the Rc/Arc magic is tucked away in hooks, and just beautiful borrowed interfaces are exposed to your code. You don't need to know how Rc/RefCell work to build a competent Dioxus app. -```rust +```rust, no_run fn app(cx: Scope) -> Element { let name = use_state(cx, || "asd"); cx.render(rsx!{ @@ -294,8 +292,7 @@ fn Button<'a>(cx: Scope<'a, Childprops<'a>>) -> Element { } ``` -There's *way* more to this story, but hopefully we've convinced you that Dioxus' DX somewhat approximates JSX/React. - +There's _way_ more to this story, but hopefully we've convinced you that Dioxus' DX somewhat approximates JSX/React. ## Dioxus is perfected for the IDE @@ -335,9 +332,10 @@ We take the performance of Dioxus seriously. Instead of resolving to "good enoug Dioxus is humbly built off the work done by [Dodrio](https://github.com/fitzgen/dodrio), a now-archived research project by fitzgen exploring the use of bump allocators in UI frameworks. -Dioxus is *substantially* more performant than many of the other Rust DOM-based UI libraries (Yew/Percy) and is *significantly* more performant than React - roughly competitive with InfernoJS. While not as performant as libraries like SolidJS/Sycamore, Dioxus imposes roughly a ~3% overhead over DOM patching, so it's *plenty* fast. +Dioxus is _substantially_ more performant than many of the other Rust DOM-based UI libraries (Yew/Percy) and is _significantly_ more performant than React - roughly competitive with InfernoJS. While not as performant as libraries like SolidJS/Sycamore, Dioxus imposes roughly a ~3% overhead over DOM patching, so it's _plenty_ fast. ## Works on Desktop and Mobile + We’ve mentioned before that Dioxus works practically anywhere that Rust does. When running natively as a desktop or mobile app, your Dioxus code will run on its own thread, not inside of a web runtime. This means you can access hardware, file system, and platform APIs directly without needing to go through a shim layer. In our examples, we feature a [file explorer app](https://github.com/DioxusLabs/example-projects/tree/master/file-explorer) and [WiFi scanner app](https://github.com/DioxusLabs/example-projects/tree/master/wifi-scanner) where platform access occurs inside an asynchronous multithreaded coroutine. This solves the problem faced by React Native and other cross-platform toolkits where JavaScript apps incur a massive performance penalty with substantial maintenance overhead associated with platform API shims. A desktop app: diff --git a/docs/reference/src/guide/custom_renderer_pt-br.md b/docs/reference/src/guide/custom_renderer_pt-br.md index fb40504a7..eada5f0a6 100644 --- a/docs/reference/src/guide/custom_renderer_pt-br.md +++ b/docs/reference/src/guide/custom_renderer_pt-br.md @@ -21,7 +21,7 @@ Para referência, confira o interpretador JavaScript ou o renderizador TUI como O tipo "DomEdit" é uma enumeração serializada que representa uma operação atômica que ocorre no `RealDom`. As variantes seguem aproximadamente este conjunto: -```rust +```rust, no_run enum DomEdit { PushRoot, AppendChildren, @@ -48,7 +48,7 @@ O mecanismo de diferenciação do Dioxus opera como uma [máquina de pilha] (htt Por uma questão de compreensão, vamos considerar este exemplo - uma declaração de interface do usuário muito simples: -```rust +```rust, no_run rsx!( h1 {"hello world"} ) ``` @@ -56,7 +56,7 @@ To get things started, Dioxus must first navigate to the container of this h1 ta When the renderer receives this instruction, it pushes the actual Node onto its own stack. The real renderer's stack will look like this: -```rust +```rust, no_run instructions: [ PushRoot(Container) ] @@ -67,7 +67,7 @@ stack: [ Em seguida, o Dioxus encontrará o nó `h1`. O algoritmo `diff` decide que este nó precisa ser criado, então o Dioxus irá gerar o DomEdit `CreateElement`. Quando o renderizador receber esta instrução, ele criará um nó desmontado e o enviará para sua própria pilha: -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -80,7 +80,7 @@ stack: [ Em seguida, Dioxus vê o nó de texto e gera o DomEdit `CreateTextNode`: -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -95,7 +95,7 @@ stack: [ Lembre-se, o nó de texto não está anexado a nada (ele está desmontado), então o Dioxus precisa gerar um `Edit` que conecte o nó de texto ao elemento `h1`. Depende da situação, mas neste caso usamos `AppendChildren`. Isso remove o nó de texto da pilha, deixando o elemento `h1` como o próximo elemento na linha. -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -110,7 +110,7 @@ stack: [ Chamamos `AppendChildren` novamente, retirando o nó `h1` e anexando-o ao pai: -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -125,7 +125,7 @@ stack: [ Finalmente, o contêiner é aberto, pois não precisamos mais dele. -```rust +```rust, no_run instructions: [ PushRoot(Container), CreateElement(h1), @@ -139,7 +139,7 @@ stack: [] Com o tempo, nossa pilha ficou assim: -```rust +```rust, no_run [] [Container] [Container, h1] @@ -165,7 +165,7 @@ Como a maioria das GUIs, o Dioxus conta com um `loop` de eventos para progredir O código para a implementação do `WebSys` é direto, então vamos adicioná-lo aqui para demonstrar como um `loop` de eventos é simples: -```rust +```rust, no_run pub async fn run(&mut self) -> dioxus_core::error::Result<()> { // Push the body element onto the WebsysDom's stack machine let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom()); @@ -195,7 +195,7 @@ pub async fn run(&mut self) -> dioxus_core::error::Result<()> { É importante que você decodifique os eventos reais do seu sistema de eventos no sistema de eventos sintético do Dioxus (significado sintético abstraído). Isso significa simplesmente combinar seu tipo de evento e criar um tipo Dioxus `UserEvent`. No momento, o sistema `VirtualEvent` é modelado quase inteiramente em torno da especificação HTML, mas estamos interessados em reduzi-lo. -```rust +```rust, no_run fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent { match event.type_().as_str() { "keydown" => { @@ -234,7 +234,7 @@ Esses elementos personalizados são definidos como `unit struct` com implementa Por exemplo, o elemento `div` é (aproximadamente!) definido assim: -```rust +```rust, no_run struct div; impl div { /// Some glorious documentation about the class property. @@ -263,7 +263,7 @@ O `RealDom` é uma abstração de nível superior sobre a atualização do DOM. Vamos construir um renderizador de brinquedo com bordas, tamanho e cor do texto. Antes de começarmos, vamos dar uma olhada em um elemento de exemplo que podemos renderizar: -```rust +```rust, no_run cx.render(rsx!{ div{ color: "red", @@ -315,7 +315,7 @@ flowchart TB Para ajudar na construção de um Dom, o núcleo nativo fornece quatro características: `State`, `ChildDepState`, `ParentDepState` e `NodeDepState` e uma estrutura `RealDom`. -```rust +```rust, no_run use dioxus_native_core::node_ref::*; use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; use dioxus_native_core_macro::{sorted_str_slice, State}; @@ -447,7 +447,7 @@ struct ToyState { Agora que temos nosso estado, podemos colocá-lo em uso em nosso DOM. Nós podemos atualizar o DOM com `update_state` para atualizar a estrutura do `DOM` (adicionando, removendo e alterando as propriedades dos nós) e então `apply_mutations` para atualizar o `ToyState` para cada um dos nós que foram alterados. -```rust +```rust, no_run fn main(){ fn app(cx: Scope) -> Element { cx.render(rsx!{ diff --git a/docs/reference/src/guide/rsx_in_depth.md b/docs/reference/src/guide/rsx_in_depth.md index 75e1f4a98..1a76f7c41 100644 --- a/docs/reference/src/guide/rsx_in_depth.md +++ b/docs/reference/src/guide/rsx_in_depth.md @@ -2,7 +2,7 @@ The RSX macro makes it very easy to assemble complex UIs with a very natural Rust syntax: -```rust +```rust, no_run rsx!( div { button { @@ -30,7 +30,7 @@ In this section, we'll cover the `rsx!` macro in depth. If you prefer to learn t Attributes must come before child elements -```rust +```rust, no_run div { hidden: "false", "some text" @@ -57,7 +57,7 @@ The `render` function provides an **extremely efficient** allocator for VNodes a Sometimes, writing `cx.render` is a hassle. The `rsx! macro will accept any token followed by a comma as the target to call "render" on: -```rust +```rust, no_run cx.render(rsx!( div {} )) // becomes render!(div {}) @@ -67,7 +67,7 @@ render!(div {}) Sometimes, you might not want to render an element given a condition. The rsx! macro will accept any tokens directly contained with curly braces, provided they resolve to a type that implements `IntoIterator`. This lets us write any Rust expression that resolves to a VNode: -```rust +```rust, no_run rsx!({ if enabled { render!(div {"enabled"}) @@ -79,7 +79,7 @@ rsx!({ A convenient way of hiding/showing an element is returning an `Option`. When combined with `and_then`, we can succinctly control the display state given some boolean: -```rust +```rust, no_run rsx!({ a.and_then(rsx!(div {"enabled"})) }) @@ -87,7 +87,7 @@ rsx!({ It's important to note that the expression `rsx!()` is typically lazy - this expression must be _rendered_ to produce a VNode. When using match statements, we must render every arm as to avoid the `no two closures are identical` rule that Rust imposes: -```rust +```rust, no_run // this will not compile! match case { true => rsx!(div {}), @@ -105,7 +105,7 @@ match case { Again, because anything that implements `IntoIterator` is valid, we can use lists directly in our `rsx!`: -```rust +```rust, no_run let items = vec!["a", "b", "c"]; cx.render(rsx!{ @@ -117,7 +117,7 @@ cx.render(rsx!{ Sometimes, it makes sense to render VNodes into a list: -```rust +```rust, no_run let mut items = vec![]; for _ in 0..5 { @@ -135,7 +135,7 @@ However, with lists, Dioxus does not exactly know how to determine which element In these cases, it is vitally important to specify a "key" alongside the element. Keys should be persistent between renders. -```rust +```rust, no_run fn render_list(cx: Scope, items: HashMap) -> DomTree { render!(ul { {items.iter().map(|key, item| { @@ -156,7 +156,7 @@ There have been many guides made for keys in React, so we recommend reading up t ### Complete Reference -```rust +```rust, no_run let text = "example"; cx.render(rsx!{ diff --git a/docs/reference/src/guide/rsx_in_depth_pt-br.md b/docs/reference/src/guide/rsx_in_depth_pt-br.md index 82c4f9298..4c981bd0a 100644 --- a/docs/reference/src/guide/rsx_in_depth_pt-br.md +++ b/docs/reference/src/guide/rsx_in_depth_pt-br.md @@ -2,7 +2,7 @@ A macro RSX facilita muito a montagem de interfaces de usuário complexas com uma sintaxe Rust muito natural: -```rust +```rust, no_run rsx!( div { button { @@ -30,7 +30,7 @@ Nesta seção, abordaremos a macro `rsx!` em profundidade. Se você preferir apr Os atributos devem vir antes dos elementos filhos -```rust +```rust, no_run div { hidden: "false", "some text" @@ -57,7 +57,7 @@ A função `render` fornece um alocador **extremamente eficiente** para `VNodes` Às vezes, escrever `cx.render` é um aborrecimento. O `rsx!` macro aceitará qualquer token seguido por uma vírgula como destino para chamar "render" em: -```rust +```rust, no_run cx.render(rsx!( div {} )) // becomes render!(div {}) @@ -67,7 +67,7 @@ render!(div {}) Às vezes, você pode não querer renderizar um elemento dada uma condição. O `rsx!` macro aceitará quaisquer tokens contidos diretamente com chaves, desde que resolvam para um tipo que implemente `IntoIterator`. Isso nos permite escrever qualquer expressão Rust que resolva para um `VNode`: -```rust +```rust, no_run rsx!({ if enabled { render!(div {"enabled"}) @@ -79,7 +79,7 @@ rsx!({ Uma maneira conveniente de ocultar/mostrar um elemento é retornar um `Option`. Quando combinado com `and_then`, podemos controlar sucintamente o estado de exibição dado alguns booleanos: -```rust +```rust, no_run rsx!({ a.and_then(rsx!(div {"enabled"})) }) @@ -87,7 +87,7 @@ rsx!({ É importante notar que a expressão `rsx!()` é tipicamente tardia - esta expressão deve ser _renderizada_ para produzir um `VNode`. Ao usar declarações de `match`, devemos renderizar todos os braços para evitar a regra 'não há dois fechamentos idênticos' que o Rust impõe: -```rust +```rust, no_run // this will not compile! match case { true => rsx!(div {}), @@ -105,7 +105,7 @@ match case { Novamente, porque qualquer coisa que implemente `IntoIterator` é válida, podemos usar listas diretamente em nosso `rsx!`: -```rust +```rust, no_run let items = vec!["a", "b", "c"]; cx.render(rsx!{ @@ -117,7 +117,7 @@ cx.render(rsx!{ Às vezes, faz sentido renderizar `VNodes` em uma lista: -```rust +```rust, no_run let mut items = vec![]; for _ in 0..5 { @@ -135,7 +135,7 @@ No entanto, com listas, Dioxus não sabe exatamente como determinar quais elemen Nesses casos, é de vital importância especificar uma "chave" ao lado do elemento. As chaves devem ser persistentes entre as renderizações. -```rust +```rust, no_run fn render_list(cx: Scope, items: HashMap) -> DomTree { render!(ul { {items.iter().map(|key, item| { @@ -156,7 +156,7 @@ Existem muitos guias feitos para chaves no React, então recomendamos a leitura ### Referência Completa -```rust +```rust, no_run let text = "example"; cx.render(rsx!{ diff --git a/docs/reference/src/platforms/desktop.md b/docs/reference/src/platforms/desktop.md index dcdcca778..30fe698ff 100644 --- a/docs/reference/src/platforms/desktop.md +++ b/docs/reference/src/platforms/desktop.md @@ -21,7 +21,7 @@ $ cargo add dioxus --features desktop Edit your `main.rs`: -```rust +```rust, no_run // main.rs use dioxus::prelude::*; From fb227943ae777fad14cf3377f65c58e9d9f6cd56 Mon Sep 17 00:00:00 2001 From: xinglixing <64331680+xinglixing@users.noreply.github.com> Date: Wed, 28 Jun 2023 12:10:18 -0700 Subject: [PATCH 506/516] Fix: bump liveview salvo version to 0.44.1. (#1224) (#1130) --- packages/liveview/Cargo.toml | 4 ++-- packages/liveview/examples/salvo.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 515d0ec54..4400541ea 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -37,7 +37,7 @@ warp = { version = "0.3.3", optional = true } axum = { version = "0.6.1", optional = true, features = ["ws"] } # salvo -salvo = { version = "0.37.7", optional = true, features = ["ws"] } +salvo = { version = "0.44.1", optional = true, features = ["ws"] } once_cell = "1.17.1" # actix is ... complicated? @@ -51,7 +51,7 @@ tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } warp = "0.3.3" axum = { version = "0.6.1", features = ["ws"] } -salvo = { version = "0.37.7", features = ["affix", "ws"] } +salvo = { version = "0.44.1", features = ["affix", "ws"] } tower = "0.4.13" [features] diff --git a/packages/liveview/examples/salvo.rs b/packages/liveview/examples/salvo.rs index 015892702..9cb8c7115 100644 --- a/packages/liveview/examples/salvo.rs +++ b/packages/liveview/examples/salvo.rs @@ -20,7 +20,8 @@ fn app(cx: Scope) -> Element { async fn main() { pretty_env_logger::init(); - let addr: SocketAddr = ([127, 0, 0, 1], 3030).into(); + let addr = "127.0.0.1:3030"; + let acceptor = TcpListener::new(addr).bind().await; let view = LiveViewPool::new(); @@ -31,7 +32,7 @@ async fn main() { println!("Listening on http://{}", addr); - Server::new(TcpListener::bind(addr)).serve(router).await; + Server::new(acceptor).serve(router).await; } #[handler] From a9d7b4d32a6fcfc1963023129f64cbfb14a19d19 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 28 Jun 2023 17:21:23 -0700 Subject: [PATCH 507/516] Prepare CLI for move into mainline --- .../cli/.github}/workflows/build.yml | 0 .../cli/.github}/workflows/docs.yml | 0 .../cli/.github}/workflows/main.yml | 0 .gitignore => packages/cli/.gitignore | 0 {.vscode => packages/cli/.vscode}/settings.json | 0 Cargo.lock => packages/cli/Cargo.lock | 0 Cargo.toml => packages/cli/Cargo.toml | 0 Dioxus.toml => packages/cli/Dioxus.toml | 0 README.md => packages/cli/README.md | 0 build.rs => packages/cli/build.rs | 0 {docs => packages/cli/docs}/.gitignore | 0 {docs => packages/cli/docs}/book.toml | 0 {docs => packages/cli/docs}/src/SUMMARY.md | 0 {docs => packages/cli/docs}/src/cmd/README.md | 0 {docs => packages/cli/docs}/src/cmd/build.md | 0 {docs => packages/cli/docs}/src/cmd/clean.md | 0 {docs => packages/cli/docs}/src/cmd/serve.md | 0 {docs => packages/cli/docs}/src/cmd/translate.md | 0 {docs => packages/cli/docs}/src/configure.md | 0 {docs => packages/cli/docs}/src/creating.md | 0 {docs => packages/cli/docs}/src/installation.md | 0 {docs => packages/cli/docs}/src/introduction.md | 0 {docs => packages/cli/docs}/src/plugin/README.md | 0 .../cli/docs}/src/plugin/interface/command.md | 0 .../cli/docs}/src/plugin/interface/dirs.md | 0 .../cli/docs}/src/plugin/interface/log.md | 0 .../cli/docs}/src/plugin/interface/network.md | 0 .../cli/docs}/src/plugin/interface/os.md | 0 .../cli/docs}/src/plugin/interface/path.md | 0 {examples => packages/cli/examples}/README.md | 0 {examples => packages/cli/examples}/plugin/init.lua | 0 .../cli/examples}/plugin/interface.lua | 0 {extension => packages/cli/extension}/.eslintrc.js | 0 {extension => packages/cli/extension}/.gitignore | 0 {extension => packages/cli/extension}/DEV.md | 0 {extension => packages/cli/extension}/LICENSE.txt | 0 {extension => packages/cli/extension}/README.md | 0 .../cli/extension}/dist/.gitignore | 0 .../cli/extension}/package-lock.json | 0 {extension => packages/cli/extension}/package.json | 0 .../cli/extension}/server/.gitignore | 0 {extension => packages/cli/extension}/src/main.ts | 0 .../cli/extension}/static/icon.png | Bin {extension => packages/cli/extension}/tsconfig.json | 0 rustfmt.toml => packages/cli/rustfmt.toml | 0 {src => packages/cli/src}/assets/autoreload.js | 0 {src => packages/cli/src}/assets/dioxus.toml | 0 {src => packages/cli/src}/assets/index.html | 0 {src => packages/cli/src}/builder.rs | 0 {src => packages/cli/src}/cargo.rs | 0 {src => packages/cli/src}/cli/autoformat/mod.rs | 0 {src => packages/cli/src}/cli/build/mod.rs | 0 {src => packages/cli/src}/cli/cfg.rs | 0 {src => packages/cli/src}/cli/clean/mod.rs | 0 {src => packages/cli/src}/cli/config/mod.rs | 0 {src => packages/cli/src}/cli/create/mod.rs | 0 {src => packages/cli/src}/cli/mod.rs | 0 {src => packages/cli/src}/cli/plugin/mod.rs | 0 {src => packages/cli/src}/cli/serve/mod.rs | 0 {src => packages/cli/src}/cli/tool/mod.rs | 0 {src => packages/cli/src}/cli/translate/mod.rs | 0 {src => packages/cli/src}/cli/version.rs | 0 {src => packages/cli/src}/config.rs | 0 {src => packages/cli/src}/error.rs | 0 {src => packages/cli/src}/lib.rs | 0 {src => packages/cli/src}/logging.rs | 0 {src => packages/cli/src}/main.rs | 0 .../cli/src}/plugin/interface/command.rs | 0 {src => packages/cli/src}/plugin/interface/dirs.rs | 0 {src => packages/cli/src}/plugin/interface/fs.rs | 0 {src => packages/cli/src}/plugin/interface/log.rs | 0 {src => packages/cli/src}/plugin/interface/mod.rs | 0 .../cli/src}/plugin/interface/network.rs | 0 {src => packages/cli/src}/plugin/interface/os.rs | 0 {src => packages/cli/src}/plugin/interface/path.rs | 0 {src => packages/cli/src}/plugin/mod.rs | 0 {src => packages/cli/src}/plugin/types.rs | 0 {src => packages/cli/src}/server/mod.rs | 0 {src => packages/cli/src}/server/proxy.rs | 0 {src => packages/cli/src}/tools.rs | 0 {tests => packages/cli/tests}/main.rs | 0 {tests => packages/cli/tests}/svg.html | 0 {tests => packages/cli/tests}/test.html | 0 83 files changed, 0 insertions(+), 0 deletions(-) rename {.github => packages/cli/.github}/workflows/build.yml (100%) rename {.github => packages/cli/.github}/workflows/docs.yml (100%) rename {.github => packages/cli/.github}/workflows/main.yml (100%) rename .gitignore => packages/cli/.gitignore (100%) rename {.vscode => packages/cli/.vscode}/settings.json (100%) rename Cargo.lock => packages/cli/Cargo.lock (100%) rename Cargo.toml => packages/cli/Cargo.toml (100%) rename Dioxus.toml => packages/cli/Dioxus.toml (100%) rename README.md => packages/cli/README.md (100%) rename build.rs => packages/cli/build.rs (100%) rename {docs => packages/cli/docs}/.gitignore (100%) rename {docs => packages/cli/docs}/book.toml (100%) rename {docs => packages/cli/docs}/src/SUMMARY.md (100%) rename {docs => packages/cli/docs}/src/cmd/README.md (100%) rename {docs => packages/cli/docs}/src/cmd/build.md (100%) rename {docs => packages/cli/docs}/src/cmd/clean.md (100%) rename {docs => packages/cli/docs}/src/cmd/serve.md (100%) rename {docs => packages/cli/docs}/src/cmd/translate.md (100%) rename {docs => packages/cli/docs}/src/configure.md (100%) rename {docs => packages/cli/docs}/src/creating.md (100%) rename {docs => packages/cli/docs}/src/installation.md (100%) rename {docs => packages/cli/docs}/src/introduction.md (100%) rename {docs => packages/cli/docs}/src/plugin/README.md (100%) rename {docs => packages/cli/docs}/src/plugin/interface/command.md (100%) rename {docs => packages/cli/docs}/src/plugin/interface/dirs.md (100%) rename {docs => packages/cli/docs}/src/plugin/interface/log.md (100%) rename {docs => packages/cli/docs}/src/plugin/interface/network.md (100%) rename {docs => packages/cli/docs}/src/plugin/interface/os.md (100%) rename {docs => packages/cli/docs}/src/plugin/interface/path.md (100%) rename {examples => packages/cli/examples}/README.md (100%) rename {examples => packages/cli/examples}/plugin/init.lua (100%) rename {examples => packages/cli/examples}/plugin/interface.lua (100%) rename {extension => packages/cli/extension}/.eslintrc.js (100%) rename {extension => packages/cli/extension}/.gitignore (100%) rename {extension => packages/cli/extension}/DEV.md (100%) rename {extension => packages/cli/extension}/LICENSE.txt (100%) rename {extension => packages/cli/extension}/README.md (100%) rename {extension => packages/cli/extension}/dist/.gitignore (100%) rename {extension => packages/cli/extension}/package-lock.json (100%) rename {extension => packages/cli/extension}/package.json (100%) rename {extension => packages/cli/extension}/server/.gitignore (100%) rename {extension => packages/cli/extension}/src/main.ts (100%) rename {extension => packages/cli/extension}/static/icon.png (100%) rename {extension => packages/cli/extension}/tsconfig.json (100%) rename rustfmt.toml => packages/cli/rustfmt.toml (100%) rename {src => packages/cli/src}/assets/autoreload.js (100%) rename {src => packages/cli/src}/assets/dioxus.toml (100%) rename {src => packages/cli/src}/assets/index.html (100%) rename {src => packages/cli/src}/builder.rs (100%) rename {src => packages/cli/src}/cargo.rs (100%) rename {src => packages/cli/src}/cli/autoformat/mod.rs (100%) rename {src => packages/cli/src}/cli/build/mod.rs (100%) rename {src => packages/cli/src}/cli/cfg.rs (100%) rename {src => packages/cli/src}/cli/clean/mod.rs (100%) rename {src => packages/cli/src}/cli/config/mod.rs (100%) rename {src => packages/cli/src}/cli/create/mod.rs (100%) rename {src => packages/cli/src}/cli/mod.rs (100%) rename {src => packages/cli/src}/cli/plugin/mod.rs (100%) rename {src => packages/cli/src}/cli/serve/mod.rs (100%) rename {src => packages/cli/src}/cli/tool/mod.rs (100%) rename {src => packages/cli/src}/cli/translate/mod.rs (100%) rename {src => packages/cli/src}/cli/version.rs (100%) rename {src => packages/cli/src}/config.rs (100%) rename {src => packages/cli/src}/error.rs (100%) rename {src => packages/cli/src}/lib.rs (100%) rename {src => packages/cli/src}/logging.rs (100%) rename {src => packages/cli/src}/main.rs (100%) rename {src => packages/cli/src}/plugin/interface/command.rs (100%) rename {src => packages/cli/src}/plugin/interface/dirs.rs (100%) rename {src => packages/cli/src}/plugin/interface/fs.rs (100%) rename {src => packages/cli/src}/plugin/interface/log.rs (100%) rename {src => packages/cli/src}/plugin/interface/mod.rs (100%) rename {src => packages/cli/src}/plugin/interface/network.rs (100%) rename {src => packages/cli/src}/plugin/interface/os.rs (100%) rename {src => packages/cli/src}/plugin/interface/path.rs (100%) rename {src => packages/cli/src}/plugin/mod.rs (100%) rename {src => packages/cli/src}/plugin/types.rs (100%) rename {src => packages/cli/src}/server/mod.rs (100%) rename {src => packages/cli/src}/server/proxy.rs (100%) rename {src => packages/cli/src}/tools.rs (100%) rename {tests => packages/cli/tests}/main.rs (100%) rename {tests => packages/cli/tests}/svg.html (100%) rename {tests => packages/cli/tests}/test.html (100%) diff --git a/.github/workflows/build.yml b/packages/cli/.github/workflows/build.yml similarity index 100% rename from .github/workflows/build.yml rename to packages/cli/.github/workflows/build.yml diff --git a/.github/workflows/docs.yml b/packages/cli/.github/workflows/docs.yml similarity index 100% rename from .github/workflows/docs.yml rename to packages/cli/.github/workflows/docs.yml diff --git a/.github/workflows/main.yml b/packages/cli/.github/workflows/main.yml similarity index 100% rename from .github/workflows/main.yml rename to packages/cli/.github/workflows/main.yml diff --git a/.gitignore b/packages/cli/.gitignore similarity index 100% rename from .gitignore rename to packages/cli/.gitignore diff --git a/.vscode/settings.json b/packages/cli/.vscode/settings.json similarity index 100% rename from .vscode/settings.json rename to packages/cli/.vscode/settings.json diff --git a/Cargo.lock b/packages/cli/Cargo.lock similarity index 100% rename from Cargo.lock rename to packages/cli/Cargo.lock diff --git a/Cargo.toml b/packages/cli/Cargo.toml similarity index 100% rename from Cargo.toml rename to packages/cli/Cargo.toml diff --git a/Dioxus.toml b/packages/cli/Dioxus.toml similarity index 100% rename from Dioxus.toml rename to packages/cli/Dioxus.toml diff --git a/README.md b/packages/cli/README.md similarity index 100% rename from README.md rename to packages/cli/README.md diff --git a/build.rs b/packages/cli/build.rs similarity index 100% rename from build.rs rename to packages/cli/build.rs diff --git a/docs/.gitignore b/packages/cli/docs/.gitignore similarity index 100% rename from docs/.gitignore rename to packages/cli/docs/.gitignore diff --git a/docs/book.toml b/packages/cli/docs/book.toml similarity index 100% rename from docs/book.toml rename to packages/cli/docs/book.toml diff --git a/docs/src/SUMMARY.md b/packages/cli/docs/src/SUMMARY.md similarity index 100% rename from docs/src/SUMMARY.md rename to packages/cli/docs/src/SUMMARY.md diff --git a/docs/src/cmd/README.md b/packages/cli/docs/src/cmd/README.md similarity index 100% rename from docs/src/cmd/README.md rename to packages/cli/docs/src/cmd/README.md diff --git a/docs/src/cmd/build.md b/packages/cli/docs/src/cmd/build.md similarity index 100% rename from docs/src/cmd/build.md rename to packages/cli/docs/src/cmd/build.md diff --git a/docs/src/cmd/clean.md b/packages/cli/docs/src/cmd/clean.md similarity index 100% rename from docs/src/cmd/clean.md rename to packages/cli/docs/src/cmd/clean.md diff --git a/docs/src/cmd/serve.md b/packages/cli/docs/src/cmd/serve.md similarity index 100% rename from docs/src/cmd/serve.md rename to packages/cli/docs/src/cmd/serve.md diff --git a/docs/src/cmd/translate.md b/packages/cli/docs/src/cmd/translate.md similarity index 100% rename from docs/src/cmd/translate.md rename to packages/cli/docs/src/cmd/translate.md diff --git a/docs/src/configure.md b/packages/cli/docs/src/configure.md similarity index 100% rename from docs/src/configure.md rename to packages/cli/docs/src/configure.md diff --git a/docs/src/creating.md b/packages/cli/docs/src/creating.md similarity index 100% rename from docs/src/creating.md rename to packages/cli/docs/src/creating.md diff --git a/docs/src/installation.md b/packages/cli/docs/src/installation.md similarity index 100% rename from docs/src/installation.md rename to packages/cli/docs/src/installation.md diff --git a/docs/src/introduction.md b/packages/cli/docs/src/introduction.md similarity index 100% rename from docs/src/introduction.md rename to packages/cli/docs/src/introduction.md diff --git a/docs/src/plugin/README.md b/packages/cli/docs/src/plugin/README.md similarity index 100% rename from docs/src/plugin/README.md rename to packages/cli/docs/src/plugin/README.md diff --git a/docs/src/plugin/interface/command.md b/packages/cli/docs/src/plugin/interface/command.md similarity index 100% rename from docs/src/plugin/interface/command.md rename to packages/cli/docs/src/plugin/interface/command.md diff --git a/docs/src/plugin/interface/dirs.md b/packages/cli/docs/src/plugin/interface/dirs.md similarity index 100% rename from docs/src/plugin/interface/dirs.md rename to packages/cli/docs/src/plugin/interface/dirs.md diff --git a/docs/src/plugin/interface/log.md b/packages/cli/docs/src/plugin/interface/log.md similarity index 100% rename from docs/src/plugin/interface/log.md rename to packages/cli/docs/src/plugin/interface/log.md diff --git a/docs/src/plugin/interface/network.md b/packages/cli/docs/src/plugin/interface/network.md similarity index 100% rename from docs/src/plugin/interface/network.md rename to packages/cli/docs/src/plugin/interface/network.md diff --git a/docs/src/plugin/interface/os.md b/packages/cli/docs/src/plugin/interface/os.md similarity index 100% rename from docs/src/plugin/interface/os.md rename to packages/cli/docs/src/plugin/interface/os.md diff --git a/docs/src/plugin/interface/path.md b/packages/cli/docs/src/plugin/interface/path.md similarity index 100% rename from docs/src/plugin/interface/path.md rename to packages/cli/docs/src/plugin/interface/path.md diff --git a/examples/README.md b/packages/cli/examples/README.md similarity index 100% rename from examples/README.md rename to packages/cli/examples/README.md diff --git a/examples/plugin/init.lua b/packages/cli/examples/plugin/init.lua similarity index 100% rename from examples/plugin/init.lua rename to packages/cli/examples/plugin/init.lua diff --git a/examples/plugin/interface.lua b/packages/cli/examples/plugin/interface.lua similarity index 100% rename from examples/plugin/interface.lua rename to packages/cli/examples/plugin/interface.lua diff --git a/extension/.eslintrc.js b/packages/cli/extension/.eslintrc.js similarity index 100% rename from extension/.eslintrc.js rename to packages/cli/extension/.eslintrc.js diff --git a/extension/.gitignore b/packages/cli/extension/.gitignore similarity index 100% rename from extension/.gitignore rename to packages/cli/extension/.gitignore diff --git a/extension/DEV.md b/packages/cli/extension/DEV.md similarity index 100% rename from extension/DEV.md rename to packages/cli/extension/DEV.md diff --git a/extension/LICENSE.txt b/packages/cli/extension/LICENSE.txt similarity index 100% rename from extension/LICENSE.txt rename to packages/cli/extension/LICENSE.txt diff --git a/extension/README.md b/packages/cli/extension/README.md similarity index 100% rename from extension/README.md rename to packages/cli/extension/README.md diff --git a/extension/dist/.gitignore b/packages/cli/extension/dist/.gitignore similarity index 100% rename from extension/dist/.gitignore rename to packages/cli/extension/dist/.gitignore diff --git a/extension/package-lock.json b/packages/cli/extension/package-lock.json similarity index 100% rename from extension/package-lock.json rename to packages/cli/extension/package-lock.json diff --git a/extension/package.json b/packages/cli/extension/package.json similarity index 100% rename from extension/package.json rename to packages/cli/extension/package.json diff --git a/extension/server/.gitignore b/packages/cli/extension/server/.gitignore similarity index 100% rename from extension/server/.gitignore rename to packages/cli/extension/server/.gitignore diff --git a/extension/src/main.ts b/packages/cli/extension/src/main.ts similarity index 100% rename from extension/src/main.ts rename to packages/cli/extension/src/main.ts diff --git a/extension/static/icon.png b/packages/cli/extension/static/icon.png similarity index 100% rename from extension/static/icon.png rename to packages/cli/extension/static/icon.png diff --git a/extension/tsconfig.json b/packages/cli/extension/tsconfig.json similarity index 100% rename from extension/tsconfig.json rename to packages/cli/extension/tsconfig.json diff --git a/rustfmt.toml b/packages/cli/rustfmt.toml similarity index 100% rename from rustfmt.toml rename to packages/cli/rustfmt.toml diff --git a/src/assets/autoreload.js b/packages/cli/src/assets/autoreload.js similarity index 100% rename from src/assets/autoreload.js rename to packages/cli/src/assets/autoreload.js diff --git a/src/assets/dioxus.toml b/packages/cli/src/assets/dioxus.toml similarity index 100% rename from src/assets/dioxus.toml rename to packages/cli/src/assets/dioxus.toml diff --git a/src/assets/index.html b/packages/cli/src/assets/index.html similarity index 100% rename from src/assets/index.html rename to packages/cli/src/assets/index.html diff --git a/src/builder.rs b/packages/cli/src/builder.rs similarity index 100% rename from src/builder.rs rename to packages/cli/src/builder.rs diff --git a/src/cargo.rs b/packages/cli/src/cargo.rs similarity index 100% rename from src/cargo.rs rename to packages/cli/src/cargo.rs diff --git a/src/cli/autoformat/mod.rs b/packages/cli/src/cli/autoformat/mod.rs similarity index 100% rename from src/cli/autoformat/mod.rs rename to packages/cli/src/cli/autoformat/mod.rs diff --git a/src/cli/build/mod.rs b/packages/cli/src/cli/build/mod.rs similarity index 100% rename from src/cli/build/mod.rs rename to packages/cli/src/cli/build/mod.rs diff --git a/src/cli/cfg.rs b/packages/cli/src/cli/cfg.rs similarity index 100% rename from src/cli/cfg.rs rename to packages/cli/src/cli/cfg.rs diff --git a/src/cli/clean/mod.rs b/packages/cli/src/cli/clean/mod.rs similarity index 100% rename from src/cli/clean/mod.rs rename to packages/cli/src/cli/clean/mod.rs diff --git a/src/cli/config/mod.rs b/packages/cli/src/cli/config/mod.rs similarity index 100% rename from src/cli/config/mod.rs rename to packages/cli/src/cli/config/mod.rs diff --git a/src/cli/create/mod.rs b/packages/cli/src/cli/create/mod.rs similarity index 100% rename from src/cli/create/mod.rs rename to packages/cli/src/cli/create/mod.rs diff --git a/src/cli/mod.rs b/packages/cli/src/cli/mod.rs similarity index 100% rename from src/cli/mod.rs rename to packages/cli/src/cli/mod.rs diff --git a/src/cli/plugin/mod.rs b/packages/cli/src/cli/plugin/mod.rs similarity index 100% rename from src/cli/plugin/mod.rs rename to packages/cli/src/cli/plugin/mod.rs diff --git a/src/cli/serve/mod.rs b/packages/cli/src/cli/serve/mod.rs similarity index 100% rename from src/cli/serve/mod.rs rename to packages/cli/src/cli/serve/mod.rs diff --git a/src/cli/tool/mod.rs b/packages/cli/src/cli/tool/mod.rs similarity index 100% rename from src/cli/tool/mod.rs rename to packages/cli/src/cli/tool/mod.rs diff --git a/src/cli/translate/mod.rs b/packages/cli/src/cli/translate/mod.rs similarity index 100% rename from src/cli/translate/mod.rs rename to packages/cli/src/cli/translate/mod.rs diff --git a/src/cli/version.rs b/packages/cli/src/cli/version.rs similarity index 100% rename from src/cli/version.rs rename to packages/cli/src/cli/version.rs diff --git a/src/config.rs b/packages/cli/src/config.rs similarity index 100% rename from src/config.rs rename to packages/cli/src/config.rs diff --git a/src/error.rs b/packages/cli/src/error.rs similarity index 100% rename from src/error.rs rename to packages/cli/src/error.rs diff --git a/src/lib.rs b/packages/cli/src/lib.rs similarity index 100% rename from src/lib.rs rename to packages/cli/src/lib.rs diff --git a/src/logging.rs b/packages/cli/src/logging.rs similarity index 100% rename from src/logging.rs rename to packages/cli/src/logging.rs diff --git a/src/main.rs b/packages/cli/src/main.rs similarity index 100% rename from src/main.rs rename to packages/cli/src/main.rs diff --git a/src/plugin/interface/command.rs b/packages/cli/src/plugin/interface/command.rs similarity index 100% rename from src/plugin/interface/command.rs rename to packages/cli/src/plugin/interface/command.rs diff --git a/src/plugin/interface/dirs.rs b/packages/cli/src/plugin/interface/dirs.rs similarity index 100% rename from src/plugin/interface/dirs.rs rename to packages/cli/src/plugin/interface/dirs.rs diff --git a/src/plugin/interface/fs.rs b/packages/cli/src/plugin/interface/fs.rs similarity index 100% rename from src/plugin/interface/fs.rs rename to packages/cli/src/plugin/interface/fs.rs diff --git a/src/plugin/interface/log.rs b/packages/cli/src/plugin/interface/log.rs similarity index 100% rename from src/plugin/interface/log.rs rename to packages/cli/src/plugin/interface/log.rs diff --git a/src/plugin/interface/mod.rs b/packages/cli/src/plugin/interface/mod.rs similarity index 100% rename from src/plugin/interface/mod.rs rename to packages/cli/src/plugin/interface/mod.rs diff --git a/src/plugin/interface/network.rs b/packages/cli/src/plugin/interface/network.rs similarity index 100% rename from src/plugin/interface/network.rs rename to packages/cli/src/plugin/interface/network.rs diff --git a/src/plugin/interface/os.rs b/packages/cli/src/plugin/interface/os.rs similarity index 100% rename from src/plugin/interface/os.rs rename to packages/cli/src/plugin/interface/os.rs diff --git a/src/plugin/interface/path.rs b/packages/cli/src/plugin/interface/path.rs similarity index 100% rename from src/plugin/interface/path.rs rename to packages/cli/src/plugin/interface/path.rs diff --git a/src/plugin/mod.rs b/packages/cli/src/plugin/mod.rs similarity index 100% rename from src/plugin/mod.rs rename to packages/cli/src/plugin/mod.rs diff --git a/src/plugin/types.rs b/packages/cli/src/plugin/types.rs similarity index 100% rename from src/plugin/types.rs rename to packages/cli/src/plugin/types.rs diff --git a/src/server/mod.rs b/packages/cli/src/server/mod.rs similarity index 100% rename from src/server/mod.rs rename to packages/cli/src/server/mod.rs diff --git a/src/server/proxy.rs b/packages/cli/src/server/proxy.rs similarity index 100% rename from src/server/proxy.rs rename to packages/cli/src/server/proxy.rs diff --git a/src/tools.rs b/packages/cli/src/tools.rs similarity index 100% rename from src/tools.rs rename to packages/cli/src/tools.rs diff --git a/tests/main.rs b/packages/cli/tests/main.rs similarity index 100% rename from tests/main.rs rename to packages/cli/tests/main.rs diff --git a/tests/svg.html b/packages/cli/tests/svg.html similarity index 100% rename from tests/svg.html rename to packages/cli/tests/svg.html diff --git a/tests/test.html b/packages/cli/tests/test.html similarity index 100% rename from tests/test.html rename to packages/cli/tests/test.html From 01aec9f062b75ad198ae9e7b501c52b7324bfd1c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 28 Jun 2023 17:24:09 -0700 Subject: [PATCH 508/516] remove fleet cruft --- .fleet/settings.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .fleet/settings.json diff --git a/.fleet/settings.json b/.fleet/settings.json deleted file mode 100644 index e69de29bb..000000000 From f1dbf033b9e21fcd6930fc69692e283306b51419 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 28 Jun 2023 17:38:08 -0700 Subject: [PATCH 509/516] Add cli to workspace --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 9320b7783..84c28b054 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "packages/dioxus", "packages/core", + "packages/cli", "packages/core-macro", "packages/router", "packages/html", From ec0fce3aef6c7b678b19f513a590713a87370539 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 28 Jun 2023 17:39:03 -0700 Subject: [PATCH 510/516] Update install location for CLI --- packages/cli/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 5a4f32a4b..6f0826e10 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -2,7 +2,7 @@

📦✨ Dioxus CLI

Tooling to supercharge Dioxus projects

-**dioxus-cli** (inspired by wasm-pack and webpack) is a tool for getting Dioxus projects up and running. +**dioxus-cli** (inspired by wasm-pack and webpack) is a tool for getting Dioxus projects up and running. It handles all build, bundling, development and publishing to simplify web development. @@ -14,7 +14,7 @@ cargo install dioxus-cli ``` ### Install from git repository ``` -cargo install --git https://github.com/DioxusLabs/cli +cargo install --git https://github.com/DioxusLabs/dioxus dioxus-cli ``` ### Install from local folder ``` @@ -22,7 +22,7 @@ cargo install --path . --debug ``` -## Get Started +## Get Started Use `dioxus create project-name` to initialize a new Dioxus project.
@@ -40,4 +40,4 @@ dioxus create hello --template gh:dioxuslabs/dioxus-template Dioxus CLI will use `Dioxus.toml` file to Identify some project info and switch some cli feature. -You can get more configure information from [Dioxus CLI Document](https://dioxuslabs.com/cli/configure.html). \ No newline at end of file +You can get more configure information from [Dioxus CLI Document](https://dioxuslabs.com/cli/configure.html). From 20c088b9903a619a32b19e0e2bc9b289610a3783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Thu, 29 Jun 2023 17:17:10 +0200 Subject: [PATCH 511/516] fix: Remove conflicting rustfmt config for cli (#1135) --- packages/cli/rustfmt.toml | 8 -------- packages/cli/src/config.rs | 24 +++++++++++++----------- 2 files changed, 13 insertions(+), 19 deletions(-) delete mode 100644 packages/cli/rustfmt.toml diff --git a/packages/cli/rustfmt.toml b/packages/cli/rustfmt.toml deleted file mode 100644 index 39b98f125..000000000 --- a/packages/cli/rustfmt.toml +++ /dev/null @@ -1,8 +0,0 @@ -version = "Two" -edition = "2021" - -imports_granularity = "Crate" -#use_small_heuristics = "Max" -#control_brace_style = "ClosingNextLine" -normalize_comments = true -format_code_in_doc_comments = true diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index 9c87e266e..438fa252e 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -188,17 +188,19 @@ impl CrateConfig { let output_filename = { match &manifest.package.as_ref().unwrap().default_run { - Some(default_run_target) => { - default_run_target.to_owned() - }, - None => { - manifest.bin.iter().find(|b| b.name == manifest.package.as_ref().map(|pkg| pkg.name.clone())) - .or(manifest.bin.iter().find(|b| b.path == Some("src/main.rs".to_owned()))) - .or(manifest.bin.first()) - .or(manifest.lib.as_ref()) - .and_then(|prod| prod.name.clone()) - .expect("No executable or library found from cargo metadata.") - } + Some(default_run_target) => default_run_target.to_owned(), + None => manifest + .bin + .iter() + .find(|b| b.name == manifest.package.as_ref().map(|pkg| pkg.name.clone())) + .or(manifest + .bin + .iter() + .find(|b| b.path == Some("src/main.rs".to_owned()))) + .or(manifest.bin.first()) + .or(manifest.lib.as_ref()) + .and_then(|prod| prod.name.clone()) + .expect("No executable or library found from cargo metadata."), } }; let executable = ExecutableType::Binary(output_filename); From f4e1d78d7a36873b76539b9a3302f8436246ae7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Thu, 29 Jun 2023 17:27:54 +0200 Subject: [PATCH 512/516] fix(docs): Fix dioxus-hooks crate description (#1134) --- packages/hooks/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hooks/Cargo.toml b/packages/hooks/Cargo.toml index 19d5e53bd..c239da698 100644 --- a/packages/hooks/Cargo.toml +++ b/packages/hooks/Cargo.toml @@ -3,7 +3,7 @@ name = "dioxus-hooks" version = "0.3.1" authors = ["Jonathan Kelley"] edition = "2018" -description = "Dioxus VirtualDOM renderer for a remote webview instance" +description = "Basic useful hooks for Dioxus." license = "MIT/Apache-2.0" repository = "https://github.com/DioxusLabs/dioxus/" homepage = "https://dioxuslabs.com" From 2b47602c2761ff58abca7951db4a6b1b9955667c Mon Sep 17 00:00:00 2001 From: Sean L <66246285+sean-tocaboca@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:31:59 +0200 Subject: [PATCH 513/516] Update README.md Fixed incorrect tailwind url --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 3963d186d..93530ba30 100644 --- a/examples/README.md +++ b/examples/README.md @@ -40,7 +40,7 @@ cargo run --example hello_world [all_css](./all_css.rs) - You can specify any CSS attribute -[tailwind](./tailwind.rs) - You can use a library for styling +[tailwind](./tailwind/) - You can use a library for styling ## Input Handling From 4ab8519af647638729281c04324fb0d453f7e570 Mon Sep 17 00:00:00 2001 From: Miles Murgaw Date: Fri, 30 Jun 2023 10:13:46 -0400 Subject: [PATCH 514/516] replace: broken test with working test --- packages/cli/src/cli/autoformat/mod.rs | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/autoformat/mod.rs b/packages/cli/src/cli/autoformat/mod.rs index 2bed439cd..24362b8d3 100644 --- a/packages/cli/src/cli/autoformat/mod.rs +++ b/packages/cli/src/cli/autoformat/mod.rs @@ -157,7 +157,33 @@ fn collect_rs_files(folder: &PathBuf, files: &mut Vec) { } } -#[test] +#[tokio::test] +async fn test_auto_fmt() { + let test_rsx = r#" + // + + rsx! { + + div {} + } + + // + // + // + + "# + .to_string(); + + let fmt = Autoformat { + check: false, + raw: Some(test_rsx), + file: None, + }; + + fmt.autoformat().await.unwrap(); +} + +/*#[test] fn spawn_properly() { let out = Command::new("dioxus") .args([ @@ -181,4 +207,4 @@ rsx! { .expect("failed to execute process"); dbg!(out); -} +}*/ From 2c8c601579ea6866245c6a840a28829ff84a669e Mon Sep 17 00:00:00 2001 From: Miles Murgaw Date: Fri, 30 Jun 2023 10:48:38 -0400 Subject: [PATCH 515/516] fix: clippy --- packages/cli/src/builder.rs | 8 +++--- packages/cli/src/cargo.rs | 2 +- packages/cli/src/cli/autoformat/mod.rs | 4 +-- packages/cli/src/cli/create/mod.rs | 14 +++++------ packages/cli/src/cli/mod.rs | 26 ++++++++++---------- packages/cli/src/cli/translate/mod.rs | 6 ++--- packages/cli/src/config.rs | 2 +- packages/cli/src/plugin/interface/command.rs | 3 ++- packages/cli/src/plugin/interface/fs.rs | 2 +- packages/cli/src/plugin/interface/path.rs | 7 +++--- packages/cli/src/plugin/mod.rs | 14 +++++------ packages/cli/src/server/mod.rs | 14 +++++------ packages/cli/src/tools.rs | 6 ++--- 13 files changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index 05935fa59..6f50fc49d 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -50,7 +50,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { log::info!("🚅 Running build command..."); let cmd = subprocess::Exec::cmd("cargo"); let cmd = cmd - .cwd(&crate_dir) + .cwd(crate_dir) .arg("build") .arg("--target") .arg("wasm32-unknown-unknown") @@ -152,7 +152,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { "-o", target_file.to_str().unwrap(), ]; - if config.release == true { + if config.release { args.push("-Oz"); } binaryen.call("wasm-opt", args)?; @@ -192,7 +192,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { config_path, ]; - if config.release == true { + if config.release { args.push("--minify"); } @@ -215,7 +215,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result { depth: 0, }; if asset_dir.is_dir() { - for entry in std::fs::read_dir(&asset_dir)? { + for entry in std::fs::read_dir(asset_dir)? { let path = entry?.path(); if path.is_file() { std::fs::copy(&path, out_dir.join(path.file_name().unwrap()))?; diff --git a/packages/cli/src/cargo.rs b/packages/cli/src/cargo.rs index d8501e8de..b52fa4b09 100644 --- a/packages/cli/src/cargo.rs +++ b/packages/cli/src/cargo.rs @@ -55,7 +55,7 @@ impl Metadata { /// TODO @Jon, find a different way that doesn't rely on the cargo metadata command (it's slow) pub fn get() -> Result { let output = Command::new("cargo") - .args(&["metadata"]) + .args(["metadata"]) .output() .map_err(|_| Error::CargoError("Manifset".to_string()))?; diff --git a/packages/cli/src/cli/autoformat/mod.rs b/packages/cli/src/cli/autoformat/mod.rs index 24362b8d3..6671b17e9 100644 --- a/packages/cli/src/cli/autoformat/mod.rs +++ b/packages/cli/src/cli/autoformat/mod.rs @@ -1,5 +1,5 @@ use futures::{stream::FuturesUnordered, StreamExt}; -use std::{fs, process::exit}; +use std::{fs, process::exit, path::Path}; use super::*; @@ -135,7 +135,7 @@ async fn autoformat_project(check: bool) -> Result<()> { Ok(()) } -fn collect_rs_files(folder: &PathBuf, files: &mut Vec) { +fn collect_rs_files(folder: &Path, files: &mut Vec) { let Ok(folder) = folder.read_dir() else { return }; // load the gitignore diff --git a/packages/cli/src/cli/create/mod.rs b/packages/cli/src/cli/create/mod.rs index 86a5e0a3f..b2323d0a2 100644 --- a/packages/cli/src/cli/create/mod.rs +++ b/packages/cli/src/cli/create/mod.rs @@ -11,9 +11,11 @@ pub struct Create { impl Create { pub fn create(self) -> Result<()> { - let mut args = GenerateArgs::default(); - args.template_path = TemplatePath { - auto_path: Some(self.template), + let args = GenerateArgs { + template_path: TemplatePath { + auto_path: Some(self.template), + ..Default::default() + }, ..Default::default() }; @@ -65,10 +67,8 @@ impl Create { fn remove_tripple_newlines(string: &str) -> String { let mut new_string = String::new(); for char in string.chars() { - if char == '\n' { - if new_string.ends_with("\n\n") { - continue; - } + if char == '\n' && new_string.ends_with("\n\n") { + continue; } new_string.push(char); } diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 0d9174bdf..9145e25bd 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -19,6 +19,7 @@ use clap::{Parser, Subcommand}; use html_parser::Dom; use serde::Deserialize; use std::{ + fmt::Display, fs::{remove_dir_all, File}, io::{Read, Write}, path::PathBuf, @@ -71,19 +72,18 @@ pub enum Commands { Plugin(plugin::Plugin), } -impl Commands { - pub fn to_string(&self) -> String { +impl Display for Commands { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Commands::Build(_) => "build", - Commands::Translate(_) => "translate", - Commands::Serve(_) => "serve", - Commands::Create(_) => "create", - Commands::Clean(_) => "clean", - Commands::Config(_) => "config", - Commands::Plugin(_) => "plugin", - Commands::Version(_) => "version", - Commands::Autoformat(_) => "fmt", + Commands::Build(_) => write!(f, "build"), + Commands::Translate(_) => write!(f, "translate"), + Commands::Serve(_) => write!(f, "serve"), + Commands::Create(_) => write!(f, "create"), + Commands::Clean(_) => write!(f, "clean"), + Commands::Config(_) => write!(f, "config"), + Commands::Plugin(_) => write!(f, "plugin"), + Commands::Version(_) => write!(f, "version"), + Commands::Autoformat(_) => write!(f, "fmt"), } - .to_string() } -} +} \ No newline at end of file diff --git a/packages/cli/src/cli/translate/mod.rs b/packages/cli/src/cli/translate/mod.rs index 67067db31..81f0da3e6 100644 --- a/packages/cli/src/cli/translate/mod.rs +++ b/packages/cli/src/cli/translate/mod.rs @@ -39,7 +39,7 @@ impl Translate { // Write the output match self.output { - Some(output) => std::fs::write(&output, out)?, + Some(output) => std::fs::write(output, out)?, None => print!("{}", out), } @@ -48,7 +48,7 @@ impl Translate { } pub fn convert_html_to_formatted_rsx(dom: &Dom, component: bool) -> String { - let callbody = rsx_rosetta::rsx_from_html(&dom); + let callbody = rsx_rosetta::rsx_from_html(dom); match component { true => write_callbody_with_icon_section(callbody), @@ -114,7 +114,7 @@ fn determine_input(file: Option, raw: Option) -> Result } if let Some(file) = file { - return Ok(std::fs::read_to_string(&file)?); + return Ok(std::fs::read_to_string(file)?); } // If neither exist, we try to read from stdin diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index 438fa252e..6324f725f 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -184,7 +184,7 @@ impl CrateConfig { None => crate_dir.join("public"), }; - let manifest = cargo_toml::Manifest::from_path(&cargo_def).unwrap(); + let manifest = cargo_toml::Manifest::from_path(cargo_def).unwrap(); let output_filename = { match &manifest.package.as_ref().unwrap().default_run { diff --git a/packages/cli/src/plugin/interface/command.rs b/packages/cli/src/plugin/interface/command.rs index 641fe328a..14a2650fe 100644 --- a/packages/cli/src/plugin/interface/command.rs +++ b/packages/cli/src/plugin/interface/command.rs @@ -2,6 +2,7 @@ use std::process::{Command, Stdio}; use mlua::{FromLua, UserData}; +#[derive(Debug, Clone, Copy)] enum StdioFromString { Inherit, Piped, @@ -41,7 +42,7 @@ impl UserData for PluginCommander { let stdout = args.1; let stderr = args.2; - if cmd.len() == 0 { + if cmd.is_empty() { return Ok(()); } let cmd_name = cmd.get(0).unwrap(); diff --git a/packages/cli/src/plugin/interface/fs.rs b/packages/cli/src/plugin/interface/fs.rs index a2e169ae2..5668dbe33 100644 --- a/packages/cli/src/plugin/interface/fs.rs +++ b/packages/cli/src/plugin/interface/fs.rs @@ -58,7 +58,7 @@ impl UserData for PluginFileSystem { let file = PathBuf::from(args.0); let target = PathBuf::from(args.1); let res = extract_zip(&file, &target); - if let Err(_) = res { + if res.is_err() { return Ok(false); } Ok(true) diff --git a/packages/cli/src/plugin/interface/path.rs b/packages/cli/src/plugin/interface/path.rs index 510ead409..dcc9071b5 100644 --- a/packages/cli/src/plugin/interface/path.rs +++ b/packages/cli/src/plugin/interface/path.rs @@ -18,10 +18,11 @@ impl UserData for PluginPath { methods.add_function("parent", |_, path: String| { let current_path = PathBuf::from(&path); let parent = current_path.parent(); - if parent.is_none() { - return Ok(path); + + if let Some(parent) = parent { + Ok(parent.to_str().unwrap().to_string()) } else { - return Ok(parent.unwrap().to_str().unwrap().to_string()); + Ok(path) } }); methods.add_function("exists", |_, path: String| { diff --git a/packages/cli/src/plugin/mod.rs b/packages/cli/src/plugin/mod.rs index 8c8233fbb..481dd0984 100644 --- a/packages/cli/src/plugin/mod.rs +++ b/packages/cli/src/plugin/mod.rs @@ -151,14 +151,12 @@ impl PluginManager { } } } + } else if let Ok(index) = name_index.get::<_, u32>(info.name.clone()) { + let _ = manager.set(index, info.clone()); } else { - if let Ok(index) = name_index.get::<_, u32>(info.name.clone()) { - let _ = manager.set(index, info.clone()); - } else { - let _ = manager.set(index, info.clone()); - index += 1; - let _ = name_index.set(info.name, index); - } + let _ = manager.set(index, info.clone()); + index += 1; + let _ = name_index.set(info.name, index); } } Err(_e) => { @@ -172,7 +170,7 @@ impl PluginManager { lua.globals().set("manager", manager).unwrap(); - return Ok(()); + Ok(()) } pub fn on_build_start(crate_config: &CrateConfig, platform: &str) -> anyhow::Result<()> { diff --git a/packages/cli/src/server/mod.rs b/packages/cli/src/server/mod.rs index a63a7e6b7..e7c62db82 100644 --- a/packages/cli/src/server/mod.rs +++ b/packages/cli/src/server/mod.rs @@ -631,7 +631,7 @@ fn print_console_info(ip: &String, port: u16, config: &CrateConfig, options: Pre "\t> Network : {}", format!("http://{}:{}/", ip, port).blue() ); - println!(""); + println!(); println!("\t> Profile : {}", profile.green()); println!("\t> Hot Reload : {}", hot_reload.cyan()); if let Some(proxies) = proxies { @@ -644,14 +644,14 @@ fn print_console_info(ip: &String, port: u16, config: &CrateConfig, options: Pre } println!("\t> Index Template : {}", custom_html_file.green()); println!("\t> URL Rewrite [index_on_404] : {}", url_rewrite.purple()); - println!(""); + println!(); println!( "\t> Build Time Use : {} millis", options.elapsed_time.to_string().green().bold() ); - println!(""); + println!(); - if options.warnings.len() == 0 { + if options.warnings.is_empty() { log::info!("{}\n", "A perfect compilation!".green().bold()); } else { log::warn!( @@ -718,9 +718,9 @@ fn get_ip() -> Option { }; match socket.local_addr() { - Ok(addr) => return Some(addr.ip().to_string()), - Err(_) => return None, - }; + Ok(addr) => Some(addr.ip().to_string()), + Err(_) => None, + } } async fn ws_handler( diff --git a/packages/cli/src/tools.rs b/packages/cli/src/tools.rs index 75ed226ce..98d4f9dbe 100644 --- a/packages/cli/src/tools.rs +++ b/packages/cli/src/tools.rs @@ -320,11 +320,11 @@ impl Tool { } pub fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { - let zip_file = std::fs::File::open(&file)?; + 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)?; + std::fs::create_dir_all(target)?; } for i in 0..zip.len() { @@ -332,7 +332,7 @@ pub fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> { if file.is_dir() { // dir let target = target.join(Path::new(&file.name().replace('\\', ""))); - let _ = std::fs::create_dir_all(target)?; + std::fs::create_dir_all(target)?; } else { // file let file_path = target.join(Path::new(file.name())); From 2ef62a9160e9ecb5a2545fadc48adffa864a5d1c Mon Sep 17 00:00:00 2001 From: Miles Murgaw Date: Fri, 30 Jun 2023 10:50:09 -0400 Subject: [PATCH 516/516] fix: rustfmt --- packages/cli/src/cli/autoformat/mod.rs | 2 +- packages/cli/src/cli/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/autoformat/mod.rs b/packages/cli/src/cli/autoformat/mod.rs index 6671b17e9..257e237f2 100644 --- a/packages/cli/src/cli/autoformat/mod.rs +++ b/packages/cli/src/cli/autoformat/mod.rs @@ -1,5 +1,5 @@ use futures::{stream::FuturesUnordered, StreamExt}; -use std::{fs, process::exit, path::Path}; +use std::{fs, path::Path, process::exit}; use super::*; diff --git a/packages/cli/src/cli/mod.rs b/packages/cli/src/cli/mod.rs index 9145e25bd..4ad4fcd89 100644 --- a/packages/cli/src/cli/mod.rs +++ b/packages/cli/src/cli/mod.rs @@ -86,4 +86,4 @@ impl Display for Commands { Commands::Autoformat(_) => write!(f, "fmt"), } } -} \ No newline at end of file +}