Merge branch 'DioxusLabs:master' into fix-borrowed-props

This commit is contained in:
ealmloff authored and GitHub committed 2023-08-19 13:35:24 -05:00
commit b2f9430689
19 files changed
+220 -84

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
name: Build CLI for Release
# Will run automatically on every new release
on:
release:
types: [published]
jobs:
build-and-upload:
permissions:
contents: write
runs-on: ${{ matrix.platform.os }}
strategy:
matrix:
platform:
- {
target: x86_64-pc-windows-msvc,
os: windows-latest,
toolchain: "1.70.0",
}
- {
target: x86_64-apple-darwin,
os: macos-latest,
toolchain: "1.70.0",
}
- {
target: x86_64-unknown-linux-gnu,
os: ubuntu-latest,
toolchain: "1.70.0",
}
steps:
- uses: actions/checkout@v3
- name: Install stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.platform.toolchain }}
targets: ${{ matrix.platform.target }}
# Setup the Github Actions Cache for the CLI package
- name: Setup cache
uses: Swatinem/rust-cache@v2
with:
workspaces: packages/cli -> ../../target
# This neat action can build and upload the binary in one go!
- name: Build and upload binary
uses: taiki-e/upload-rust-binary-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
target: ${{ matrix.platform.target }}
bin: dx
archive: dx-${{ matrix.platform.target }}
checksum: sha256
manifest_path: packages/cli/Cargo.toml
+1 -1
View File
@@ -40,7 +40,7 @@
<span> | </span>
<a href="https://github.com/DioxusLabs/example-projects"> Examples </a>
<span> | </span>
<a href="https://dioxuslabs.com/docs/0.3/guide/en/"> Guide </a>
<a href="https://dioxuslabs.com/learn/0.4/guide"> Guide </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/dioxus/blob/master/notes/README/ZH_CN.md"> 中文 </a>
<span> | </span>
+2 -1
View File
@@ -89,7 +89,8 @@ fn app(cx: Scope) -> Element {
key: None,
// The static template this node will use. The template is stored in a Cell so it can be replaced with a new template when hot rsx reloading is enabled
template: std::cell::Cell::new(TEMPLATE),
root_ids: Default::default(),
root_ids: dioxus::core::exports::bumpalo::collections::Vec::new_in(__cx.bump())
.into(),
dynamic_nodes: __cx.bump().alloc([
// The dynamic count text node (dynamic node id 0)
__cx.text_node(format_args!("High-Five counter: {0}", count)),
@@ -51,6 +51,8 @@ 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.
> Note about forms: if an event handler is attached to the `onsubmit` event of a form, default behavior is to **not submit it**, meaning having `prevent_default: "onsubmit"` will submit it in this case.
## 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:
@@ -64,3 +64,5 @@ Então, você pode usá-lo como qualquer outro manipulador:
> Nota: assim como qualquer outro atributo, você pode nomear os manipuladores como quiser! Embora eles devam começar com `on`, para que o prop seja automaticamente transformado em um `EventHandler` no local da chamada.
>
> Você também pode colocar dados personalizados no evento, em vez de, por exemplo, `MouseData`
> Nota sobre formulários: se um manipulador de evento está anexado ao evento `onsubmit` em um formulário, o comportamento padrão é de **não submetê-lo**. Portanto, especificar `prevent_default: "onsubmit"` irá submetê-lo.
+6
View File
@@ -100,3 +100,9 @@ name = "dx"
[dev-dependencies]
tempfile = "3.3"
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/dx-{ target }{ archive-suffix }"
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-fmt = "zip"
+8 -5
View File
@@ -93,18 +93,21 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
// [2] Establish the output directory structure
let bindgen_outdir = out_dir.join("assets").join("dioxus");
let release_type = match config.release {
true => "release",
false => "debug",
let build_profile = if config.custom_profile.is_some() {
config.custom_profile.as_ref().unwrap()
} else if config.release {
"release"
} else {
"debug"
};
let input_path = match executable {
ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir
.join(format!("wasm32-unknown-unknown/{}", release_type))
.join(format!("wasm32-unknown-unknown/{}", build_profile))
.join(format!("{}.wasm", name)),
ExecutableType::Example(name) => target_dir
.join(format!("wasm32-unknown-unknown/{}/examples", release_type))
.join(format!("wasm32-unknown-unknown/{}/examples", build_profile))
.join(format!("{}.wasm", name)),
};
+62 -61
View File
@@ -5,7 +5,7 @@ use crate::{
output::{print_console_info, PrettierOptions, WebServerInfo},
setup_file_watcher, setup_file_watcher_hot_reload,
},
BuildResult, CrateConfig, Result,
BuildResult, CrateConfig, Result, WebHttpsConfig,
};
use axum::{
body::{Full, HttpBody},
@@ -218,67 +218,12 @@ async fn get_rustls(config: &CrateConfig) -> Result<Option<RustlsConfig>> {
return Ok(None);
}
let (cert_path, key_path) = match web_config.mkcert {
let (cert_path, key_path) = if let Some(true) = web_config.mkcert {
// mkcert, use it
Some(true) => {
// Get paths to store certs, otherwise use ssl/item.pem
let key_path = web_config
.key_path
.clone()
.unwrap_or(DEFAULT_KEY_PATH.to_string());
let cert_path = web_config
.cert_path
.clone()
.unwrap_or(DEFAULT_CERT_PATH.to_string());
// Create ssl directory if using defaults
if key_path == DEFAULT_KEY_PATH && cert_path == DEFAULT_CERT_PATH {
_ = fs::create_dir("ssl");
}
let cmd = Command::new("mkcert")
.args([
"-install",
"-key-file",
&key_path,
"-cert-file",
&cert_path,
"localhost",
"::1",
"127.0.0.1",
])
.spawn();
match cmd {
Err(e) => {
match e.kind() {
io::ErrorKind::NotFound => log::error!("mkcert is not installed. See https://github.com/FiloSottile/mkcert#installation for installation instructions."),
e => log::error!("an error occured while generating mkcert certificates: {}", e.to_string()),
};
return Err("failed to generate mkcert certificates".into());
}
Ok(mut cmd) => {
cmd.wait()?;
}
}
(cert_path, key_path)
}
// not mkcert
Some(false) => {
// get paths to cert & key
if let (Some(key), Some(cert)) =
(web_config.key_path.clone(), web_config.cert_path.clone())
{
(cert, key)
} else {
// missing cert or key
return Err("https is enabled but cert or key path is missing".into());
}
}
// other
_ => return Ok(None),
get_rustls_with_mkcert(web_config)?
} else {
// if mkcert not specified or false, don't use it
get_rustls_without_mkcert(web_config)?
};
Ok(Some(
@@ -286,6 +231,62 @@ async fn get_rustls(config: &CrateConfig) -> Result<Option<RustlsConfig>> {
))
}
fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> {
// Get paths to store certs, otherwise use ssl/item.pem
let key_path = web_config
.key_path
.clone()
.unwrap_or(DEFAULT_KEY_PATH.to_string());
let cert_path = web_config
.cert_path
.clone()
.unwrap_or(DEFAULT_CERT_PATH.to_string());
// Create ssl directory if using defaults
if key_path == DEFAULT_KEY_PATH && cert_path == DEFAULT_CERT_PATH {
_ = fs::create_dir("ssl");
}
let cmd = Command::new("mkcert")
.args([
"-install",
"-key-file",
&key_path,
"-cert-file",
&cert_path,
"localhost",
"::1",
"127.0.0.1",
])
.spawn();
match cmd {
Err(e) => {
match e.kind() {
io::ErrorKind::NotFound => log::error!("mkcert is not installed. See https://github.com/FiloSottile/mkcert#installation for installation instructions."),
e => log::error!("an error occured while generating mkcert certificates: {}", e.to_string()),
};
return Err("failed to generate mkcert certificates".into());
}
Ok(mut cmd) => {
cmd.wait()?;
}
}
Ok((cert_path, key_path))
}
fn get_rustls_without_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> {
// get paths to cert & key
if let (Some(key), Some(cert)) = (web_config.key_path.clone(), web_config.cert_path.clone()) {
Ok((cert, key))
} else {
// missing cert or key
Err("https is enabled but cert or key path is missing".into())
}
}
/// Sets up and returns a router
async fn setup_router(
config: CrateConfig,
+6 -2
View File
@@ -87,8 +87,12 @@ impl<'b> VirtualDom {
}
}
// Intialize the root nodes slice
*node.root_ids.borrow_mut() = vec![ElementId(0); node.template.get().roots.len()];
// Initialize the root nodes slice
{
let mut nodes_mut = node.root_ids.borrow_mut();
let len = node.template.get().roots.len();
nodes_mut.resize(len, ElementId::default());
};
// The best renderers will have templates prehydrated and registered
// Just in case, let's create the template using instructions anyways
+9 -1
View File
@@ -15,6 +15,7 @@ use DynamicNode::*;
impl<'b> VirtualDom {
pub(super) fn diff_scope(&mut self, scope: ScopeId) {
self.runtime.scope_stack.borrow_mut().push(scope);
let scope_state = &mut self.get_scope(scope).unwrap();
unsafe {
// Load the old and new bump arenas
@@ -45,6 +46,7 @@ impl<'b> VirtualDom {
(Aborted(l), Ready(r)) => self.replace_placeholder(l, [r]),
};
}
self.runtime.scope_stack.borrow_mut().pop();
}
fn diff_ok_to_err(&mut self, l: &'b VNode<'b>, p: &'b VPlaceholder) {
@@ -126,7 +128,13 @@ impl<'b> VirtualDom {
});
// Make sure the roots get transferred over while we're here
*right_template.root_ids.borrow_mut() = left_template.root_ids.borrow().clone();
{
let mut right = right_template.root_ids.borrow_mut();
right.clear();
for &element in left_template.root_ids.borrow().iter() {
right.push(element);
}
}
let root_ids = right_template.root_ids.borrow();
+7 -7
View File
@@ -54,7 +54,7 @@ pub struct VNode<'a> {
/// The IDs for the roots of this template - to be used when moving the template around and removing it from
/// the actual Dom
pub root_ids: RefCell<Vec<ElementId>>,
pub root_ids: RefCell<bumpalo::collections::Vec<'a, ElementId>>,
/// The dynamic parts of the template
pub dynamic_nodes: &'a [DynamicNode<'a>],
@@ -65,11 +65,11 @@ pub struct VNode<'a> {
impl<'a> VNode<'a> {
/// Create a template with no nodes that will be skipped over during diffing
pub fn empty() -> Element<'a> {
pub fn empty(cx: &'a ScopeState) -> Element<'a> {
Some(VNode {
key: None,
parent: None,
root_ids: Default::default(),
root_ids: RefCell::new(bumpalo::collections::Vec::new_in(cx.bump())),
dynamic_nodes: &[],
dynamic_attrs: &[],
template: Cell::new(Template {
@@ -698,7 +698,7 @@ impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
impl<'a, 'b> IntoDynNode<'b> for &'a str {
fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
DynamicNode::Text(VText {
value: bumpalo::collections::String::from_str_in(self, cx.bump()).into_bump_str(),
value: cx.bump().alloc_str(self),
id: Default::default(),
})
}
@@ -741,10 +741,10 @@ impl<'a> IntoTemplate<'a> for VNode<'a> {
}
}
impl<'a> IntoTemplate<'a> for Element<'a> {
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
fn into_template(self, cx: &'a ScopeState) -> VNode<'a> {
match self {
Some(val) => val.into_template(_cx),
_ => VNode::empty().unwrap(),
Some(val) => val.into_template(cx),
_ => VNode::empty(cx).unwrap(),
}
}
}
+2 -2
View File
@@ -179,7 +179,7 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode {
node_paths: &[&[0]],
attr_paths: &[],
}),
root_ids: Default::default(),
root_ids: bumpalo::collections::Vec::new_in(cx.bump()).into(),
dynamic_nodes: cx.bump().alloc([cx.component(
create_random_element,
DepthProps { depth, root: false },
@@ -276,7 +276,7 @@ fn create_random_element(cx: Scope<DepthProps>) -> Element {
key: None,
parent: None,
template: Cell::new(template),
root_ids: Default::default(),
root_ids: bumpalo::collections::Vec::new_in(cx.bump()).into(),
dynamic_nodes: {
let dynamic_nodes: Vec<_> = dynamic_node_types
.iter()
+14
View File
@@ -282,6 +282,20 @@ impl<T> UseSharedState<T> {
),
}
}
/// Take a reference to the inner value temporarily and produce a new value
#[cfg_attr(debug_assertions, track_caller)]
#[cfg_attr(debug_assertions, inline(never))]
pub fn with<O>(&self, immutable_callback: impl FnOnce(&T) -> O) -> O {
immutable_callback(&*self.read())
}
/// Take a mutable reference to the inner value temporarily and produce a new value
#[cfg_attr(debug_assertions, track_caller)]
#[cfg_attr(debug_assertions, inline(never))]
pub fn with_mut<O>(&self, mutable_callback: impl FnOnce(&mut T) -> O) -> O {
mutable_callback(&mut *self.write())
}
}
impl<T> Clone for UseSharedState<T> {
+10 -1
View File
@@ -1,4 +1,4 @@
use std::{collections::HashMap, fmt::Debug};
use std::{any::Any, collections::HashMap, fmt::Debug};
use dioxus_core::Event;
@@ -45,6 +45,12 @@ impl FileEngine for SerializedFileEngine {
.await
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
}
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
self.read_file(file)
.await
.map(|val| Box::new(val) as Box<dyn Any>)
}
}
#[cfg(feature = "serialize")]
@@ -89,6 +95,9 @@ pub trait FileEngine {
// read a file to string
async fn read_file_to_string(&self, file: &str) -> Option<String>;
// returns a file in platform's native representation
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
}
impl_event! {
@@ -1,3 +1,4 @@
use std::any::Any;
use std::path::PathBuf;
use crate::FileEngine;
@@ -40,4 +41,9 @@ impl FileEngine for NativeFileEngine {
Some(contents)
}
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
let file = File::open(file).await.ok()?;
Some(Box::new(file))
}
}
+3 -2
View File
@@ -187,7 +187,7 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode {
node_paths: &[&[0]],
attr_paths: &[],
}),
root_ids: Default::default(),
root_ids: dioxus::core::exports::bumpalo::collections::Vec::new_in(cx.bump()).into(),
dynamic_nodes: cx.bump().alloc([cx.component(
create_random_element,
DepthProps { depth, root: false },
@@ -257,7 +257,8 @@ fn create_random_element(cx: Scope<DepthProps>) -> Element {
key: None,
parent: None,
template: Cell::new(template),
root_ids: Default::default(),
root_ids: dioxus::core::exports::bumpalo::collections::Vec::new_in(cx.bump())
.into(),
dynamic_nodes: {
let dynamic_nodes: Vec<_> = dynamic_node_types
.iter()
+2 -1
View File
@@ -231,6 +231,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
// Render and release the mutable borrow on context
let roots = quote! { #( #root_printer ),* };
let root_count = self.roots.len();
let node_printer = &context.dynamic_nodes;
let dyn_attr_printer = &context.dynamic_attributes;
let node_paths = context.node_paths.iter().map(|it| quote!(&[#(#it),*]));
@@ -247,7 +248,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
parent: None,
key: #key_tokens,
template: std::cell::Cell::new(TEMPLATE),
root_ids: Default::default(),
root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(#root_count, __cx.bump()).into(),
dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]),
dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]),
}
+23
View File
@@ -1,3 +1,5 @@
use std::any::Any;
use dioxus_html::FileEngine;
use futures_channel::oneshot;
use js_sys::Uint8Array;
@@ -100,4 +102,25 @@ impl FileEngine for WebFileEngine {
None
}
}
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
let file = self.find(file)?;
Some(Box::new(file))
}
}
/// Helper trait for WebFileEngine
#[async_trait::async_trait(?Send)]
pub trait WebFileEngineExt {
/// returns web_sys::File
async fn get_web_file(&self, file: &str) -> Option<web_sys::File>;
}
#[async_trait::async_trait(?Send)]
impl WebFileEngineExt for std::sync::Arc<dyn FileEngine> {
async fn get_web_file(&self, file: &str) -> Option<web_sys::File> {
let native_file = self.get_native_file(file).await?;
let ret = native_file.downcast::<web_sys::File>().ok()?;
Some(*ret)
}
}
+1
View File
@@ -54,6 +54,7 @@
// - Do DOM work in the next requestAnimationFrame callback
pub use crate::cfg::Config;
pub use crate::file_engine::WebFileEngineExt;
use dioxus_core::{Element, Scope, VirtualDom};
use futures_util::{
future::{select, Either},