Compare commits

..

1 Commits
v0.6.9 ... 2237

Author SHA1 Message Date
Greg Johnston
bcaa102d8d fix: correctly track source in create_local_resource 2024-01-27 19:49:00 -05:00
151 changed files with 503 additions and 2372 deletions

View File

@@ -29,4 +29,4 @@ jobs:
with:
directory: ${{ matrix.directory }}
cargo_make_task: "ci"
toolchain: nightly-2024-01-29
toolchain: nightly

View File

@@ -24,4 +24,4 @@ jobs:
with:
directory: ${{ matrix.directory }}
cargo_make_task: "ci"
toolchain: nightly-2024-01-29
toolchain: nightly

View File

@@ -40,4 +40,4 @@ jobs:
with:
directory: ${{ matrix.directory }}
cargo_make_task: "ci"
toolchain: nightly-2024-01-29
toolchain: nightly

View File

@@ -31,9 +31,10 @@ jobs:
dir_names: true
dir_names_max_depth: "2"
files: |
examples/**
!examples/cargo-make/**
!examples/gtk/**
examples
!examples/cargo-make
!examples/gtk
!examples/hackernews_js_fetch
!examples/Makefile.toml
!examples/*.md
json: true

View File

@@ -25,8 +25,8 @@ jobs:
with:
files: |
examples/**
!examples/cargo-make/**
!examples/gtk/**
!examples/cargo-make
!examples/gtk
!examples/Makefile.toml
!examples/*.md

View File

@@ -55,9 +55,9 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 18
- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
@@ -107,11 +107,6 @@ jobs:
fi
done
- name: Install Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
# Run Cargo Make Task
- name: ${{ inputs.cargo_make_task }}
run: |

View File

@@ -25,23 +25,22 @@ members = [
exclude = ["benchmarks", "examples"]
[workspace.package]
version = "0.6.9"
rust-version = "1.75"
version = "0.6.3"
[workspace.dependencies]
leptos = { path = "./leptos", version = "0.6.9" }
leptos_dom = { path = "./leptos_dom", version = "0.6.9" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.6.9" }
leptos_macro = { path = "./leptos_macro", version = "0.6.9" }
leptos_reactive = { path = "./leptos_reactive", version = "0.6.9" }
leptos_server = { path = "./leptos_server", version = "0.6.9" }
server_fn = { path = "./server_fn", version = "0.6.9" }
server_fn_macro = { path = "./server_fn_macro", version = "0.6.9" }
leptos = { path = "./leptos", version = "0.6.3" }
leptos_dom = { path = "./leptos_dom", version = "0.6.3" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.6.3" }
leptos_macro = { path = "./leptos_macro", version = "0.6.3" }
leptos_reactive = { path = "./leptos_reactive", version = "0.6.3" }
leptos_server = { path = "./leptos_server", version = "0.6.3" }
server_fn = { path = "./server_fn", version = "0.6.3" }
server_fn_macro = { path = "./server_fn_macro", version = "0.6.3" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.6" }
leptos_config = { path = "./leptos_config", version = "0.6.9" }
leptos_router = { path = "./router", version = "0.6.9" }
leptos_meta = { path = "./meta", version = "0.6.9" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.6.9" }
leptos_config = { path = "./leptos_config", version = "0.6.3" }
leptos_router = { path = "./router", version = "0.6.3" }
leptos_meta = { path = "./meta", version = "0.6.3" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.6.3" }
[profile.release]
codegen-units = 1

View File

@@ -150,7 +150,7 @@ There are several people in the community using Leptos right now for internal ap
### Can I use this for native GUI?
Sure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive any native GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:
Sure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive native any GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:
- Use signals, derived signals, and memos to create your reactive system
- Create GUI widgets

View File

@@ -2,7 +2,6 @@
name = "benchmarks"
version = "0.1.0"
edition = "2021"
rust-version.workspace = true
[dependencies]
l0410 = { package = "leptos", version = "0.4.10", features = [

View File

@@ -3,5 +3,5 @@ alias = "check-all"
[tasks.check-all]
command = "cargo"
args = ["+nightly-2024-01-29", "check-all-features"]
args = ["+nightly", "check-all-features"]
install_crate = "cargo-all-features"

View File

@@ -3,5 +3,5 @@ alias = "test-all"
[tasks.test-all]
command = "cargo"
args = ["+nightly-2024-01-29", "test-all-features"]
args = ["+nightly", "test-all-features"]
install_crate = "cargo-all-features"

View File

@@ -51,5 +51,103 @@ echo "CARGO_MAKE_CRATE_WORKSPACE_MEMBERS = $examples"
[tasks.test-report]
workspace = false
description = "show the cargo-make configuration for web examples [web|all|help]"
script = { file = "./cargo-make/scripts/web-report.sh" }
description = "report web testing technology used by examples - OPTION: [all]"
script = '''
set -emu
BOLD="\e[1m"
GREEN="\e[0;32m"
ITALIC="\e[3m"
YELLOW="\e[0;33m"
RESET="\e[0m"
echo
echo "${YELLOW}Web Test Technology${RESET}"
echo
makefile_paths=$(find . -name Makefile.toml -not -path '*/target/*' -not -path '*/node_modules/*' |
sed 's%./%%' |
sed 's%/Makefile.toml%%' |
grep -v Makefile.toml |
sort -u)
start_path=$(pwd)
for path in $makefile_paths; do
cd $path
crate_symbols=
pw_count=$(find . -name playwright.config.ts | wc -l)
while read -r line; do
case $line in
*"cucumber"*)
crate_symbols=$crate_symbols"C"
;;
*"fantoccini"*)
crate_symbols=$crate_symbols"D"
;;
esac
done <"./Cargo.toml"
while read -r line; do
case $line in
*"cargo-make/wasm-test.toml"*)
crate_symbols=$crate_symbols"W"
;;
*"cargo-make/playwright-test.toml"*)
crate_symbols=$crate_symbols"P"
crate_symbols=$crate_symbols"N"
;;
*"cargo-make/playwright-trunk-test.toml"*)
crate_symbols=$crate_symbols"P"
crate_symbols=$crate_symbols"T"
;;
*"cargo-make/trunk_server.toml"*)
crate_symbols=$crate_symbols"T"
;;
*"cargo-make/cargo-leptos-webdriver-test.toml"*)
crate_symbols=$crate_symbols"L"
;;
*"cargo-make/cargo-leptos-test.toml"*)
crate_symbols=$crate_symbols"L"
if [ $pw_count -gt 0 ]; then
crate_symbols=$crate_symbols"P"
fi
;;
esac
done <"./Makefile.toml"
# Sort list of tools
sorted_crate_symbols=$(echo ${crate_symbols} | grep -o . | sort | tr -d "\n")
formatted_crate_symbols="${BOLD}${YELLOW}${sorted_crate_symbols}${RESET}"
crate_line=$path
if [ ! -z ${1+x} ]; then
# Show all examples
if [ ! -z $crate_symbols ]; then
crate_line=$crate_line$formatted_crate_symbols
fi
echo $crate_line
elif [ ! -z $crate_symbols ]; then
# Filter out examples that do not run tests in `ci`
crate_line=$crate_line$formatted_crate_symbols
echo $crate_line
fi
cd ${start_path}
done
c="${BOLD}${YELLOW}C${RESET} = Cucumber"
d="${BOLD}${YELLOW}D${RESET} = WebDriver"
l="${BOLD}${YELLOW}L${RESET} = Cargo Leptos"
n="${BOLD}${YELLOW}N${RESET} = Node"
p="${BOLD}${YELLOW}P${RESET} = Playwright"
t="${BOLD}${YELLOW}T${RESET} = Trunk"
w="${BOLD}${YELLOW}W${RESET} = WASM"
echo
echo "${ITALIC}Keys:${RESET} $c, $d, $l, $n, $p, $t, $w"
echo
'''

View File

@@ -16,7 +16,7 @@ You can also run any of the examples using [`cargo-make`](https://github.com/sag
Follow these steps to get any example up and running.
1. `cd` to the example you want to run
1. `cd` to the example root directory
2. Run `cargo make ci` to setup and test the example
3. Run `cargo make start` to run the example
4. Open the client URL in the console output (<http://127.0.0.1:8080> or <http://127.0.0.1:3000> by default)

View File

@@ -15,13 +15,13 @@ clear = true
dependencies = ["check-debug", "check-release"]
[tasks.check-debug]
toolchain = "nightly-2024-01-29"
toolchain = "nightly"
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
[tasks.check-release]
toolchain = "nightly-2024-01-29"
toolchain = "nightly"
command = "cargo"
args = ["check-all-features", "--release"]
install_crate = "cargo-all-features"

View File

@@ -3,36 +3,32 @@
[tasks.stop-client]
condition = { env_set = ["CLIENT_PROCESS_NAME"] }
script = '''
if pidof -q ${CLIENT_PROCESS_NAME}; then
echo " Stopping ${CLIENT_PROCESS_NAME}"
if [ ! -z $(pidof ${CLIENT_PROCESS_NAME}) ]; then
pkill -ef ${CLIENT_PROCESS_NAME}
else
echo " ${CLIENT_PROCESS_NAME} is already stopped"
fi
'''
[tasks.client-status]
condition = { env_set = ["CLIENT_PROCESS_NAME"] }
script = '''
if pidof -q ${CLIENT_PROCESS_NAME}; then
echo " ${CLIENT_PROCESS_NAME} is up"
else
if [ -z $(pidof ${CLIENT_PROCESS_NAME}) ]; then
echo " ${CLIENT_PROCESS_NAME} is not running"
else
echo " ${CLIENT_PROCESS_NAME} is up"
fi
'''
[tasks.maybe-start-client]
condition = { env_set = ["CLIENT_PROCESS_NAME"] }
script = '''
if pidof -q ${CLIENT_PROCESS_NAME}; then
echo " ${CLIENT_PROCESS_NAME} is already started"
else
if [ -z $(pidof ${CLIENT_PROCESS_NAME}) ]; then
echo " Starting ${CLIENT_PROCESS_NAME}"
if [ -n "${SPAWN_CLIENT_PROCESS}" ];then
echo "Spawning process..."
if [ -z ${SPAWN_CLIENT_PROCESS} ];then
cargo make start-client ${@} &
else
cargo make start-client ${@}
fi
else
echo " ${CLIENT_PROCESS_NAME} is already started"
fi
'''

View File

@@ -1,11 +1,11 @@
[tasks.build]
toolchain = "nightly-2024-01-29"
toolchain = "nightly"
command = "cargo"
args = ["build-all-features"]
install_crate = "cargo-all-features"
[tasks.check]
toolchain = "nightly-2024-01-29"
toolchain = "nightly"
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"

View File

@@ -1,24 +0,0 @@
[tasks.build]
clear = true
command = "deno"
args = ["task", "build"]
[tasks.start-client]
command = "deno"
args = ["task", "start"]
[tasks.check]
clear = true
dependencies = ["check-debug", "check-release"]
[tasks.check-debug]
toolchain = "nightly-2024-01-29"
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
[tasks.check-release]
toolchain = "nightly-2024-01-29"
command = "cargo"
args = ["check-all-features", "--release"]
install_crate = "cargo-all-features"

View File

@@ -1,5 +1,5 @@
[tasks.pre-clippy]
env = { CARGO_MAKE_CLIPPY_ARGS = "--no-deps --all-targets --all-features -- -D warnings" }
env = { CARGO_MAKE_CLIPPY_ARGS = "--all-targets --all-features -- -D warnings" }
[tasks.check-style]
dependencies = ["check-format-flow", "clippy-flow"]

View File

@@ -6,17 +6,9 @@ extend = [
[tasks.integration-test]
description = "Run integration test with automated start and stop of processes"
env = { SPAWN_CLIENT_PROCESS = "1" }
run_task = { name = ["start", "wait-test-stop"], parallel = true }
dependencies = ["start", "wait-one", "test-playwright", "stop"]
[tasks.wait-test-stop]
private = true
dependencies = ["wait-server", "test-playwright", "stop"]
[tasks.wait-server]
[tasks.wait-one]
script = '''
for run in {1..12}; do
echo "Waiting to ensure server is started..."
sleep 10
done
echo "Times up, running tests"
sleep 1
'''

View File

@@ -1,176 +0,0 @@
#!/bin/bash
set -emu
BOLD="\e[1m"
ITALIC="\e[3m"
YELLOW="\e[1;33m"
BLUE="\e[1;36m"
RESET="\e[0m"
function web { #task: only include examples with web cargo-make configuration
print_header
print_crate_tags "$@"
print_footer
}
function all { #task: includes all examples
print_header
print_crate_tags "all"
print_footer
}
function print_header {
echo -e "${YELLOW}Cargo Make Web Report${RESET}"
echo
echo -e "${ITALIC}Show how crates are configured to run and test web examples with cargo-make${RESET}"
echo
}
function print_crate_tags {
local makefile_paths
makefile_paths=$(find_makefile_lines)
local start_path
start_path=$(pwd)
for path in $makefile_paths; do
cd "$path"
local crate_tags=
# Add cargo tags
while read -r line; do
case $line in
*"cucumber"*)
crate_tags=$crate_tags"C"
;;
*"fantoccini"*)
crate_tags=$crate_tags"F"
;;
*"package.metadata.leptos"*)
crate_tags=$crate_tags"M"
;;
esac
done <"./Cargo.toml"
#Add makefile tags
local pw_count
pw_count=$(find . -name playwright.config.ts | wc -l)
while read -r line; do
case $line in
*"cargo-make/wasm-test.toml"*)
crate_tags=$crate_tags"W"
;;
*"cargo-make/playwright-test.toml"*)
crate_tags=$crate_tags"P"
crate_tags=$crate_tags"N"
;;
*"cargo-make/playwright-trunk-test.toml"*)
crate_tags=$crate_tags"P"
crate_tags=$crate_tags"T"
;;
*"cargo-make/trunk_server.toml"*)
crate_tags=$crate_tags"T"
;;
*"cargo-make/cargo-leptos-webdriver-test.toml"*)
crate_tags=$crate_tags"L"
;;
*"cargo-make/cargo-leptos-test.toml"*)
crate_tags=$crate_tags"L"
if [ "$pw_count" -gt 0 ]; then
crate_tags=$crate_tags"P"
fi
;;
*"cargo-make/cargo-leptos.toml"*)
crate_tags=$crate_tags"L"
;;
*"cargo-make/deno-build.toml"*)
crate_tags=$crate_tags"D"
;;
esac
done <"./Makefile.toml"
# Sort tags
local keys
keys=$(echo "$crate_tags" | grep -o . | sort | tr -d "\n")
# Find leptos projects that are not configured to build with cargo-leptos
keys=${keys//"LM"/"L"}
# Find leptos projects that are not configured to build with deno
keys=${keys//"DM"/"D"}
# Maybe print line
local crate_line=$path
if [ -n "$crate_tags" ]; then
local color=$YELLOW
case $keys in
*"M"*)
color=$BLUE
;;
esac
crate_line="$crate_line${color}$keys${RESET}"
echo -e "$crate_line"
elif [ "$#" -gt 0 ]; then
crate_line="${BOLD}$crate_line${RESET}"
echo -e "$crate_line"
fi
cd "$start_path"
done
}
function find_makefile_lines {
find . -name Makefile.toml -not -path '*/target/*' -not -path '*/node_modules/*' |
sed 's%./%%' |
sed 's%/Makefile.toml%%' |
grep -v Makefile.toml |
sort -u
}
function print_footer {
c="${BOLD}${YELLOW}C${RESET} = Cucumber Test Runner"
d="${BOLD}${YELLOW}D${RESET} = Deno"
f="${BOLD}${YELLOW}F${RESET} = Fantoccini WebDriver"
l="${BOLD}${YELLOW}L${RESET} = Cargo Leptos"
m="${BOLD}${BLUE}M${RESET} = Cargo Leptos Metadata Only (${ITALIC}ci is not configured to build with cargo-leptos or deno${RESET})"
n="${BOLD}${YELLOW}N${RESET} = Node"
p="${BOLD}${YELLOW}P${RESET} = Playwright Test"
t="${BOLD}${YELLOW}T${RESET} = Trunk"
w="${BOLD}${YELLOW}W${RESET} = WASM Test"
echo
echo -e "${ITALIC}Report Keys:${RESET}\n $c\n $d\n $f\n $l\n $m\n $n\n $p\n $t\n $w"
echo
}
###################
# HELP
###################
function list_help_for {
local task=$1
grep -E "^function.+ #$task" "$0" |
sed 's/function/ /' |
sed -e "s| { #$task: |~|g" |
column -s"~" -t |
sort
}
function help { #help: show task descriptions
echo -e "${BOLD}Usage:${RESET} ./$(basename "$0") <task> [options]"
echo
echo "Show the cargo-make configuration for web examples"
echo
echo -e "${BOLD}Tasks:${RESET}"
list_help_for task
echo
}
TIMEFORMAT="./web-report.sh completed in %3lR"
time "${@:-all}" # Show the report by default

View File

@@ -3,21 +3,18 @@
[tasks.stop-server]
condition = { env_set = ["SERVER_PROCESS_NAME"] }
script = '''
if pidof -q ${SERVER_PROCESS_NAME}; then
echo " Stopping ${SERVER_PROCESS_NAME}"
if [ ! -z $(pidof ${SERVER_PROCESS_NAME}) ]; then
pkill -ef ${SERVER_PROCESS_NAME}
else
echo " ${SERVER_PROCESS_NAME} is already stopped"
fi
'''
[tasks.server-status]
condition = { env_set = ["SERVER_PROCESS_NAME"] }
script = '''
if pidof -q ${SERVER_PROCESS_NAME}; then
echo " ${SERVER_PROCESS_NAME} is up"
else
if [ -z $(pidof ${SERVER_PROCESS_NAME}) ]; then
echo " ${SERVER_PROCESS_NAME} is not running"
else
echo " ${SERVER_PROCESS_NAME} is up"
fi
'''
@@ -27,11 +24,11 @@ script = '''
YELLOW="\e[0;33m"
RESET="\e[0m"
if pidof -q ${SERVER_PROCESS_NAME}; then
echo " ${SERVER_PROCESS_NAME} is already started"
else
if [ -z $(pidof ${SERVER_PROCESS_NAME}) ]; then
echo " Starting ${SERVER_PROCESS_NAME}"
echo " ${YELLOW}>> Run cargo make stop to end process${RESET}"
cargo make start-server ${@} &
else
echo " ${SERVER_PROCESS_NAME} is already started"
fi
'''

View File

@@ -6,33 +6,25 @@ script = '''
RESET="\e[0m"
if command -v chromedriver; then
if pidof -q chromedriver; then
echo " chromedriver is already started"
else
echo "Starting chomedriver"
if [ -z $(pidof chromedriver) ]; then
chromedriver --port=4444 &
fi
else
echo "${RED}${BOLD}ERROR${RESET} - chromedriver not found"
echo "${RED}${BOLD}ERROR${RESET} - chromedriver is required by this task"
exit 1
fi
'''
[tasks.stop-webdriver]
script = '''
if pidof -q chromedriver; then
echo " Stopping chromedriver"
pkill -ef "chromedriver"
else
echo " chromedriver is already stopped"
fi
pkill -f "chromedriver"
'''
[tasks.webdriver-status]
script = '''
if pidof -q chromedriver; then
echo chromedriver is up
else
if [ -z $(pidof chromedriver) ]; then
echo chromedriver is not running
else
echo chromedriver is up
fi
'''

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -69,7 +69,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -132,6 +132,15 @@ pub fn Counter() -> impl IntoView {
|_| get_server_count(),
);
let value =
move || counter.get().map(|count| count.unwrap_or(0)).unwrap_or(0);
let error_msg = move || {
counter.get().and_then(|res| match res {
Ok(_) => None,
Err(e) => Some(e),
})
};
view! {
<div>
<h2>"Simple Counter"</h2>
@@ -141,24 +150,15 @@ pub fn Counter() -> impl IntoView {
<div>
<button on:click=move |_| clear.dispatch(())>"Clear"</button>
<button on:click=move |_| dec.dispatch(())>"-1"</button>
<span>
"Value: "
<Suspense>
{move || counter.and_then(|count| *count)} "!"
</Suspense>
</span>
<span>"Value: " {value} "!"</span>
<button on:click=move |_| inc.dispatch(())>"+1"</button>
</div>
<Suspense>
{move || {
counter.get().and_then(|res| match res {
Ok(_) => None,
Err(e) => Some(e),
}).map(|msg| {
view! { <p>"Error: " {msg.to_string()}</p> }
})
}}
</Suspense>
{move || {
error_msg()
.map(|msg| {
view! { <p>"Error: " {msg.to_string()}</p> }
})
}}
</div>
}
}
@@ -204,7 +204,7 @@ pub fn FormCounter() -> impl IntoView {
<input type="hidden" name="msg" value="form value down"/>
<input type="submit" value="-1"/>
</ActionForm>
<span>"Value: " <Suspense>{move || value().to_string()} "!"</Suspense></span>
<span>"Value: " {move || value().to_string()} "!"</span>
<ActionForm action=adjust>
<input type="hidden" name="delta" value="1"/>
<input type="hidden" name="msg" value="form value up"/>

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -2,7 +2,6 @@
name = "counter_without_macros"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
[profile.release]
codegen-units = 1

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -2,7 +2,6 @@
name = "counters_stable"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
[dependencies]
leptos = { path = "../../leptos", features = ["csr"] }

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,6 +1,5 @@
use leptos::{ev::click, html::AnyElement, *};
// no extra parameter
pub fn highlight(el: HtmlElement<AnyElement>) {
let mut highlighted = false;
@@ -15,7 +14,6 @@ pub fn highlight(el: HtmlElement<AnyElement>) {
});
}
// one extra parameter
pub fn copy_to_clipboard(el: HtmlElement<AnyElement>, content: &str) {
let content = content.to_string();
@@ -33,35 +31,6 @@ pub fn copy_to_clipboard(el: HtmlElement<AnyElement>, content: &str) {
});
}
// custom parameter
#[derive(Clone)]
pub struct Amount(usize);
impl From<usize> for Amount {
fn from(value: usize) -> Self {
Self(value)
}
}
// a 'default' value if no value is passed in
impl From<()> for Amount {
fn from(_: ()) -> Self {
Self(1)
}
}
// .into() will automatically be called on the parameter
pub fn add_dot(el: HtmlElement<AnyElement>, amount: Amount) {
_ = el.clone().on(click, move |_| {
el.set_inner_text(&format!(
"{}{}",
el.inner_text(),
".".repeat(amount.0)
))
})
}
#[component]
pub fn SomeComponent() -> impl IntoView {
view! {
@@ -77,11 +46,6 @@ pub fn App() -> impl IntoView {
view! {
<a href="#" use:copy_to_clipboard=data>"Copy \"" {data} "\" to clipboard"</a>
// automatically applies the directive to every root element in `SomeComponent`
<SomeComponent use:highlight />
// no value will default to `().into()`
<button use:add_dot>"Add a dot"</button>
// `5.into()` automatically called
<button use:add_dot=5>"Add 5 dots"</button>
}
}

View File

@@ -8,7 +8,7 @@ See the [Examples README](../README.md) for setup and run instructions.
## Testing
This project is configured to run start and stop of processes for integration tests without the use of Cargo Leptos or Node.
This project is configured to run start and stop of processes for integration tests wihtout the use of Cargo Leptos or Node.
## Quick Start

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -61,7 +61,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -0,0 +1,8 @@
[env]
VERIFY_GTK = false
[tasks.verify-flow]
condition = { env_set = ["VERIFY_GTK"] }
[tasks.verify]
condition = { env_set = ["VERIFY_GTK"] }

View File

@@ -70,7 +70,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -62,18 +62,16 @@ pub fn Stories() -> impl IntoView {
}}
</span>
<span>"page " {page}</span>
<Suspense>
<span class="page-link"
class:disabled=hide_more_link
aria-hidden=hide_more_link
<span class="page-link"
class:disabled=hide_more_link
aria-hidden=hide_more_link
>
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
aria-label="Next Page"
>
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
aria-label="Next Page"
>
"more >"
</a>
</span>
</Suspense>
"more >"
</a>
</span>
</div>
<main class="news-list">
<div>

View File

@@ -71,7 +71,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -81,7 +81,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,8 +1 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/cargo-leptos.toml" },
]
[env]
CLIENT_PROCESS_NAME = "hackernews_islands"
extend = [{ path = "../cargo-make/main.toml" }]

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -78,7 +78,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,8 +1 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/deno-build.toml" },
]
[env]
CLIENT_PROCESS_NAME = "deno"
extend = [{ path = "../cargo-make/main.toml" }]

View File

@@ -5,13 +5,13 @@ extend = [
]
[tasks.build]
toolchain = "nightly-2024-01-29"
toolchain = "nightly"
command = "cargo"
args = ["build-all-features", "--target", "wasm32-unknown-unknown"]
install_crate = "cargo-all-features"
[tasks.check]
toolchain = "nightly-2024-01-29"
toolchain = "nightly"
command = "cargo"
args = ["check-all-features", "--target", "wasm32-unknown-unknown"]
install_crate = "cargo-all-features"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -15,7 +15,7 @@ leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] }
server_fn = { path = "../../server_fn", features = ["serde-lite", "rkyv", "multipart"] }
server_fn = { path = "../../server_fn", features = ["serde-lite", "rkyv", "multipart" ]}
log = "0.4"
simple_logger = "4.0"
serde = { version = "1", features = ["derive"] }
@@ -31,9 +31,6 @@ web-sys = { version = "0.3.67", features = ["FileList", "File"] }
strum = { version = "0.25.0", features = ["strum_macros", "derive"] }
notify = { version = "6.1.1", optional = true }
pin-project-lite = "0.2.13"
dashmap = { version = "5.5.3", optional = true }
once_cell = { version = "1.19.0", optional = true }
async-broadcast = { version = "0.6.0", optional = true }
[features]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
@@ -46,10 +43,7 @@ ssr = [
"leptos_meta/ssr",
"leptos_router/ssr",
"dep:leptos_axum",
"dep:notify",
"dep:dashmap",
"dep:once_cell",
"dep:async-broadcast",
"dep:notify"
]
[package.metadata.cargo-all-features]
@@ -77,7 +71,7 @@ end2end-cmd = "cargo make test-ui"
end2end-dir = "e2e"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -5,15 +5,13 @@ use leptos_meta::{provide_meta_context, Link, Meta, Stylesheet};
use leptos_router::{ActionForm, Route, Router, Routes};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use server_fn::{
client::{browser::BrowserClient, Client},
codec::{
Encoding, FromReq, FromRes, GetUrl, IntoReq, IntoRes, MultipartData,
MultipartFormData, Rkyv, SerdeLite, StreamingText, TextStream,
},
request::{browser::BrowserRequest, ClientReq, Req},
response::{browser::BrowserResponse, ClientRes, Res},
request::{ClientReq, Req},
response::{ClientRes, Res},
};
use std::future::Future;
#[cfg(feature = "ssr")]
use std::sync::{
atomic::{AtomicU8, Ordering},
@@ -57,10 +55,8 @@ pub fn HomePage() -> impl IntoView {
<ServerFnArgumentExample/>
<RkyvExample/>
<FileUpload/>
<FileUploadWithProgress/>
<FileWatcher/>
<CustomEncoding/>
<CustomClientExample/>
}
}
@@ -320,7 +316,7 @@ pub fn RkyvExample() -> impl IntoView {
set_input(value);
}
>
Click to capitalize
Click to see length
</button>
<p>{input}</p>
<Transition>
@@ -335,8 +331,8 @@ pub fn FileUpload() -> impl IntoView {
///
/// On the server, this uses the `multer` crate, which provides a streaming API.
#[server(
input = MultipartFormData,
)]
input = MultipartFormData,
)]
pub async fn file_length(
data: MultipartData,
) -> Result<usize, ServerFnError> {
@@ -394,168 +390,6 @@ pub fn FileUpload() -> impl IntoView {
}
}
/// This component uses server functions to upload a file, while streaming updates on the upload
/// progress.
#[component]
pub fn FileUploadWithProgress() -> impl IntoView {
/// In theory, you could create a single server function which
/// 1) received multipart form data
/// 2) returned a stream that contained updates on the progress
///
/// In reality, browsers do not actually support duplexing requests in this way. In other
/// words, every existing browser actually requires that the request stream be complete before
/// it begins processing the response stream.
///
/// Instead, we can create two separate server functions:
/// 1) one that receives multipart form data and begins processing the upload
/// 2) a second that returns a stream of updates on the progress
///
/// This requires us to store some global state of all the uploads. In a real app, you probably
/// shouldn't do exactly what I'm doing here in the demo. For example, this map just
/// distinguishes between files by filename, not by user.
#[cfg(feature = "ssr")]
mod progress {
use async_broadcast::{broadcast, Receiver, Sender};
use dashmap::DashMap;
use futures::Stream;
use once_cell::sync::Lazy;
struct File {
total: usize,
tx: Sender<usize>,
rx: Receiver<usize>,
}
static FILES: Lazy<DashMap<String, File>> = Lazy::new(DashMap::new);
pub async fn add_chunk(filename: &str, len: usize) {
println!("[{filename}]\tadding {len}");
let mut entry =
FILES.entry(filename.to_string()).or_insert_with(|| {
println!("[{filename}]\tinserting channel");
let (tx, rx) = broadcast(128);
File { total: 0, tx, rx }
});
entry.total += len;
let new_total = entry.total;
// we're about to do an async broadcast, so we don't want to hold a lock across it
let tx = entry.tx.clone();
drop(entry);
// now we send the message and don't have to worry about it
tx.broadcast(new_total)
.await
.expect("couldn't send a message over channel");
}
pub fn for_file(filename: &str) -> impl Stream<Item = usize> {
let entry =
FILES.entry(filename.to_string()).or_insert_with(|| {
println!("[{filename}]\tinserting channel");
let (tx, rx) = broadcast(128);
File { total: 0, tx, rx }
});
entry.rx.clone()
}
}
#[server(
input = MultipartFormData,
)]
pub async fn upload_file(data: MultipartData) -> Result<(), ServerFnError> {
let mut data = data.into_inner().unwrap();
while let Ok(Some(mut field)) = data.next_field().await {
let name =
field.file_name().expect("no filename on field").to_string();
while let Ok(Some(chunk)) = field.chunk().await {
let len = chunk.len();
println!("[{name}]\t{len}");
progress::add_chunk(&name, len).await;
// in a real server function, you'd do something like saving the file here
}
}
Ok(())
}
#[server(output = StreamingText)]
pub async fn file_progress(
filename: String,
) -> Result<TextStream, ServerFnError> {
println!("getting progress on {filename}");
// get the stream of current length for the file
let progress = progress::for_file(&filename);
// separate each number with a newline
// the HTTP response might pack multiple lines of this into a single chunk
// we need some way of dividing them up
let progress = progress.map(|bytes| Ok(format!("{bytes}\n")));
Ok(TextStream::new(progress))
}
let (filename, set_filename) = create_signal(None);
let (max, set_max) = create_signal(None);
let (current, set_current) = create_signal(None);
let on_submit = move |ev: SubmitEvent| {
ev.prevent_default();
let target = ev.target().unwrap().unchecked_into::<HtmlFormElement>();
let form_data = FormData::new_with_form(&target).unwrap();
let file = form_data
.get("file_to_upload")
.unchecked_into::<web_sys::File>();
let filename = file.name();
let size = file.size() as usize;
set_filename(Some(filename.clone()));
set_max(Some(size));
set_current(None::<usize>);
spawn_local(async move {
let mut progress = file_progress(filename)
.await
.expect("couldn't initialize stream")
.into_inner();
while let Some(Ok(len)) = progress.next().await {
// the TextStream from the server function will be a series of `usize` values
// however, the response itself may pack those chunks into a smaller number of
// chunks, each with more text in it
// so we've padded them with newspace, and will split them out here
// each value is the latest total, so we'll just take the last one
let len = len
.split('\n')
.filter(|n| !n.is_empty())
.last()
.expect(
"expected at least one non-empty value from \
newline-delimited rows",
)
.parse::<usize>()
.expect("invalid length");
set_current(Some(len));
}
});
spawn_local(async move {
upload_file(form_data.into())
.await
.expect("couldn't upload file");
});
};
view! {
<h3>File Upload with Progress</h3>
<p>A file upload with progress can be handled with two separate server functions.</p>
<aside>See the doc comment on the component for an explanation.</aside>
<form on:submit=on_submit>
<input type="file" name="file_to_upload"/>
<input type="submit"/>
</form>
{move || filename().map(|filename| view! { <p>Uploading {filename}</p> })}
{move || max().map(|max| view! {
<progress max=max value=move || current().unwrap_or_default()/>
})}
}
}
#[component]
pub fn FileWatcher() -> impl IntoView {
#[server(input = GetUrl, output = StreamingText)]
@@ -798,55 +632,3 @@ pub fn CustomEncoding() -> impl IntoView {
<p>{result}</p>
}
}
/// Middleware lets you modify the request/response on the server.
///
/// On the client, you might also want to modify the request. For example, you may need to add a
/// custom header for authentication on every request. You can do this by creating a "custom
/// client."
#[component]
pub fn CustomClientExample() -> impl IntoView {
// Define a type for our client.
pub struct CustomClient;
// Implement the `Client` trait for it.
impl<CustErr> Client<CustErr> for CustomClient {
// BrowserRequest and BrowserResponse are the defaults used by other server functions.
// They are wrappers for the underlying Web Fetch API types.
type Request = BrowserRequest;
type Response = BrowserResponse;
// Our custom `send()` implementation does all the work.
fn send(
req: Self::Request,
) -> impl Future<Output = Result<Self::Response, ServerFnError<CustErr>>>
+ Send {
// BrowserRequest derefs to the underlying Request type from gloo-net,
// so we can get access to the headers here
let headers = req.headers();
// modify the headers by appending one
headers.append("X-Custom-Header", "foobar");
// delegate back out to BrowserClient to send the modified request
BrowserClient::send(req)
}
}
// Specify our custom client with `client = `
#[server(client = CustomClient)]
pub async fn fn_with_custom_client() -> Result<(), ServerFnError> {
use http::header::HeaderMap;
use leptos_axum::extract;
let headers: HeaderMap = extract().await?;
let custom_header = headers.get("X-Custom-Header");
println!("X-Custom-Header = {custom_header:?}");
Ok(())
}
view! {
<h3>Custom clients</h3>
<p>You can define a custom server function client to do something like adding a header to every request.</p>
<p>Check the network request in your browser devtools to see how this client adds a custom header.</p>
<button on:click=|_| spawn_local(async { fn_with_custom_client().await.unwrap() })>Click me</button>
}
}

View File

@@ -19,7 +19,7 @@ leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4"
simple_logger = "4.0"
serde = { version = "1.0", features = ["derive"] }
axum = { version = "0.7", optional = true, features = ["macros"] }
axum = { version = "0.7", optional = true, features=["macros"] }
tower = { version = "0.4", optional = true }
tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1", features = ["full"], optional = true }
@@ -30,10 +30,10 @@ sqlx = { version = "0.7.2", features = [
], optional = true }
thiserror = "1.0"
wasm-bindgen = "0.2"
axum_session_auth = { version = "0.12.1", features = [
axum_session_auth = { version = "0.10", features = [
"sqlite-rustls",
], optional = true }
axum_session = { version = "0.12.4", features = [
axum_session = { version = "0.10", features = [
"sqlite-rustls",
], optional = true }
bcrypt = { version = "0.15", optional = true }
@@ -83,7 +83,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -51,7 +51,7 @@ pub mod ssr {
.await
.ok()?;
//lets just get all the tokens the user can use, we will only use the full permissions if modifying them.
//lets just get all the tokens the user can use, we will only use the full permissions if modifing them.
let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(
"SELECT token FROM user_permissions WHERE user_id = ?;",
)
@@ -75,7 +75,7 @@ pub mod ssr {
.await
.ok()?;
//lets just get all the tokens the user can use, we will only use the full permissions if modifying them.
//lets just get all the tokens the user can use, we will only use the full permissions if modifing them.
let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(
"SELECT token FROM user_permissions WHERE user_id = ?;",
)

View File

@@ -70,7 +70,7 @@ async fn main() {
SessionConfig::default().with_table_name("axum_sessions");
let auth_config = AuthConfig::<i64>::default();
let session_store = SessionStore::<SessionSqlitePool>::new(
Some(SessionSqlitePool::from(pool.clone())),
Some(pool.clone().into()),
session_config,
)
.await

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,6 +1,6 @@
use leptos::*;
// Slots are created in similar manner to components, except that they use the #[slot] macro.
// Slots are created in simillar manner to components, except that they use the #[slot] macro.
#[slot]
struct Then {
children: ChildrenFn,

View File

@@ -86,7 +86,7 @@ reload-port = 3001
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -47,7 +47,7 @@ pub mod ssr_imports {
.await
.ok()?;
//lets just get all the tokens the user can use, we will only use the full permissions if modifying them.
//lets just get all the tokens the user can use, we will only use the full permissions if modifing them.
let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(
"SELECT token FROM user_permissions WHERE user_id = ?;",
)
@@ -71,7 +71,7 @@ pub mod ssr_imports {
.await
.ok()?;
//lets just get all the tokens the user can use, we will only use the full permissions if modifying them.
//lets just get all the tokens the user can use, we will only use the full permissions if modifing them.
let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(
"SELECT token FROM user_permissions WHERE user_id = ?;",
)

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -9,13 +9,12 @@ use thiserror::Error;
pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context();
let fallback = || view! { "Page not found." }.into_view();
view! {
<Stylesheet id="leptos" href="/pkg/ssr_modes.css"/>
<Title text="Welcome to Leptos"/>
<Router fallback>
<Router>
<main>
<Routes>
// Well load the home page with out-of-order streaming and <Suspense/>

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -9,13 +9,12 @@ use thiserror::Error;
pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context();
let fallback = || view! { "Page not found." }.into_view();
view! {
<Stylesheet id="leptos" href="/pkg/ssr_modes.css"/>
<Title text="Welcome to Leptos"/>
<Router fallback>
<Router>
<main>
<Routes>
// Well load the home page with out-of-order streaming and <Suspense/>

View File

@@ -8,6 +8,7 @@ crate-type = ["cdylib", "rlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_actix = { path = "../../integrations/actix", optional = true }
@@ -16,12 +17,12 @@ leptos_router = { path = "../../router", features = ["nightly"] }
gloo-net = { version = "0.2", features = ["http"] }
log = "0.4"
# dependencies for client (enable when csr or hydrate set)
# dependecies for client (enable when csr or hydrate set)
wasm-bindgen = { version = "0.2", optional = true }
console_log = { version = "1", optional = true }
console_error_panic_hook = { version = "0.1", optional = true }
# dependencies for server (enable when ssr set)
# dependecies for server (enable when ssr set)
actix-files = { version = "0.6", optional = true }
actix-web = { version = "4", features = ["macros"], optional = true }
futures = { version = "0.3", optional = true }
@@ -96,7 +97,7 @@ end2end-cmd = "npx playwright test"
end2end-dir = "end2end"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -44,7 +44,7 @@ skip_feature_sets = [["ssr", "hydrate"]]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name = "leptos_tailwind"
output-name = "tailwind_axum"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
site-root = "target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written

View File

@@ -1,4 +1,4 @@
# Leptos with Axum + TailwindCSS Template
# Leptos with Axum + TailwindCSS Tempate
This is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/leptos-rs/leptos) web framework, Axum server, and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.

View File

@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"preline": "^1.8.0",
"tailwindcss": "^3.3.2"
}
},
@@ -103,6 +104,15 @@
"node": ">= 8"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -689,6 +699,14 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/preline": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/preline/-/preline-1.8.0.tgz",
"integrity": "sha512-guttn86Fc/+AbvN9oKcr2z3zU7DL3Q5dl7nhcR4nTi5F02LXQc7WIYwgIXMR97kymCs52feiju6glXO3dUIpvA==",
"dependencies": {
"@popperjs/core": "^2.11.2"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -7,7 +7,8 @@ pub fn App() -> impl IntoView {
provide_meta_context();
view! {
<Stylesheet id="leptos" href="/pkg/leptos_tailwind.css"/>
<Stylesheet id="leptos" href="/pkg/tailwind_axum.css"/>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Router>
<Routes>

View File

@@ -10,7 +10,9 @@ leptos_router = { path = "../../router", features = ["csr", "nightly"] }
log = "0.4"
gloo-net = { version = "0.2", features = ["http"] }
# dependencies for client (enable when csr or hydrate set)
# dependecies for client (enable when csr or hydrate set)
wasm-bindgen = { version = "0.2" }
console_log = { version = "1" }
console_error_panic_hook = { version = "0.1" }
console_log = { version = "1"}
console_error_panic_hook = { version = "0.1"}

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -8,7 +8,7 @@ pub fn App() -> impl IntoView {
view! {
<Stylesheet id="leptos" href="/style/output.css"/>
<Stylesheet id="leptos" href="/pkg/tailwind.css"/>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Router>
<Routes>

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -67,7 +67,7 @@ end2end-cmd = "cargo make test-ui"
end2end-dir = "e2e"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -69,7 +69,7 @@ end2end-cmd = "cargo make test-ui"
end2end-dir = "e2e"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -70,7 +70,7 @@ end2end-cmd = "cargo make test-ui"
end2end-dir = "e2e"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly-2024-01-29"
channel = "nightly"

View File

@@ -7,8 +7,9 @@ pub struct Todos(pub Vec<Todo>);
const STORAGE_KEY: &str = "todos-leptos";
impl Default for Todos {
fn default() -> Self {
// Basic operations to manipulate the todo list: nothing really interesting here
impl Todos {
pub fn new() -> Self {
let starting_todos =
window()
.local_storage()
@@ -22,10 +23,7 @@ impl Default for Todos {
.unwrap_or_default();
Self(starting_todos)
}
}
// Basic operations to manipulate the todo list: nothing really interesting here
impl Todos {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
@@ -88,6 +86,12 @@ impl Todos {
}
}
impl Default for Todos {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Todo {
pub id: Uuid,
@@ -132,7 +136,7 @@ const ENTER_KEY: u32 = 13;
#[component]
pub fn TodoMVC() -> impl IntoView {
// The `todos` are a signal, since we need to reactively update the list
let (todos, set_todos) = create_signal(Todos::default());
let (todos, set_todos) = create_signal(Todos::new());
// We provide a context that each <Todo/> component can use to update the list
// Here, I'm just passing the `WriteSignal`; a <Todo/> doesn't need to read the whole list

View File

@@ -6,7 +6,6 @@ authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Actix integrations for the Leptos web framework."
rust-version.workspace = true
[dependencies]
actix-http = "3"

View File

@@ -150,6 +150,14 @@ pub fn redirect(path: &str) {
to redirect()."
);
}
if let Some(response_options) = use_context::<ResponseOptions>() {
response_options.set_status(StatusCode::FOUND);
response_options.insert_header(
header::LOCATION,
header::HeaderValue::from_str(path)
.expect("Failed to create HeaderValue"),
);
}
}
/// An Actix [struct@Route](actix_web::Route) that listens for a `POST` request with
@@ -203,7 +211,7 @@ pub fn handle_server_fns() -> Route {
/// context, allowing you to pass in info about the route or user from Actix, or other info.
///
/// **NOTE**: If your server functions expect a context, make sure to provide it both in
/// [`handle_server_fns_with_context`] **and** in [`LeptosRoutes::leptos_routes_with_context`] (or whatever
/// [`handle_server_fns_with_context`] **and** in [`leptos_routes_with_context`] (or whatever
/// rendering method you are using). During SSR, server functions are called by the rendering
/// method, while subsequent calls from the client are handled by the server function handler.
/// The same context needs to be provided to both handlers.
@@ -1215,18 +1223,13 @@ where
let mode = listing.mode();
for method in listing.methods() {
let additional_context = additional_context.clone();
let additional_context_and_method = move || {
provide_context(method);
additional_context();
};
router = if let Some(static_mode) = listing.static_mode() {
router.route(
path,
static_route(
options.clone(),
app_fn.clone(),
additional_context_and_method.clone(),
additional_context.clone(),
method,
static_mode,
),
@@ -1238,7 +1241,7 @@ where
SsrMode::OutOfOrder => {
render_app_to_stream_with_context(
options.clone(),
additional_context_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
method,
)
@@ -1246,7 +1249,7 @@ where
SsrMode::PartiallyBlocked => {
render_app_to_stream_with_context_and_replace_blocks(
options.clone(),
additional_context_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
method,
true,
@@ -1255,14 +1258,14 @@ where
SsrMode::InOrder => {
render_app_to_stream_in_order_with_context(
options.clone(),
additional_context_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
method,
)
}
SsrMode::Async => render_app_async_with_context(
options.clone(),
additional_context_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
method,
),
@@ -1366,7 +1369,7 @@ impl LeptosRoutes for &mut ServiceConfig {
}
}
/// A helper to make it easier to use Actix extractors in server functions.
/// A helper to make it easier to use Axum extractors in server functions.
///
/// It is generic over some type `T` that implements [`FromRequest`] and can
/// therefore be used in an extractor. The compiler can often infer this type.

View File

@@ -1,148 +0,0 @@
use leptos::*;
use leptos_actix::generate_route_list;
use leptos_router::{Route, Router, Routes, TrailingSlash};
#[component]
fn DefaultApp() -> impl IntoView {
let view = || view! { "" };
view! {
<Router>
<Routes>
<Route path="/foo" view/>
<Route path="/bar/" view/>
<Route path="/baz/:id" view/>
<Route path="/baz/:name/" view/>
<Route path="/baz/*any" view/>
</Routes>
</Router>
}
}
#[test]
fn test_default_app() {
let routes = generate_route_list(DefaultApp);
// We still have access to the original (albeit normalized) Leptos paths:
assert_same(
&routes,
|r| r.leptos_path(),
&["/bar", "/baz/*any", "/baz/:id", "/baz/:name", "/foo"],
);
// ... But leptos-actix has also reformatted "paths" to work for Actix.
assert_same(
&routes,
|r| r.path(),
&["/bar", "/baz/{id}", "/baz/{name}", "/baz/{tail:.*}", "/foo"],
);
}
#[component]
fn ExactApp() -> impl IntoView {
let view = || view! { "" };
let trailing_slash = TrailingSlash::Exact;
view! {
<Router trailing_slash>
<Routes>
<Route path="/foo" view/>
<Route path="/bar/" view/>
<Route path="/baz/:id" view/>
<Route path="/baz/:name/" view/>
<Route path="/baz/*any" view/>
</Routes>
</Router>
}
}
#[test]
fn test_exact_app() {
let routes = generate_route_list(ExactApp);
// In Exact mode, the Leptos paths no longer have their trailing slashes stripped:
assert_same(
&routes,
|r| r.leptos_path(),
&["/bar/", "/baz/*any", "/baz/:id", "/baz/:name/", "/foo"],
);
// Actix paths also have trailing slashes as a result:
assert_same(
&routes,
|r| r.path(),
&[
"/bar/",
"/baz/{id}",
"/baz/{name}/",
"/baz/{tail:.*}",
"/foo",
],
);
}
#[component]
fn RedirectApp() -> impl IntoView {
let view = || view! { "" };
let trailing_slash = TrailingSlash::Redirect;
view! {
<Router trailing_slash>
<Routes>
<Route path="/foo" view/>
<Route path="/bar/" view/>
<Route path="/baz/:id" view/>
<Route path="/baz/:name/" view/>
<Route path="/baz/*any" view/>
</Routes>
</Router>
}
}
#[test]
fn test_redirect_app() {
let routes = generate_route_list(RedirectApp);
assert_same(
&routes,
|r| r.leptos_path(),
&[
"/bar",
"/bar/",
"/baz/*any",
"/baz/:id",
"/baz/:id/",
"/baz/:name",
"/baz/:name/",
"/foo",
"/foo/",
],
);
// ... But leptos-actix has also reformatted "paths" to work for Actix.
assert_same(
&routes,
|r| r.path(),
&[
"/bar",
"/bar/",
"/baz/{id}",
"/baz/{id}/",
"/baz/{name}",
"/baz/{name}/",
"/baz/{tail:.*}",
"/foo",
"/foo/",
],
);
}
fn assert_same<'t, T, F, U>(
input: &'t Vec<T>,
mapper: F,
expected_sorted_values: &[U],
) where
F: Fn(&'t T) -> U + 't,
U: Ord + std::fmt::Debug,
{
let mut values: Vec<U> = input.iter().map(mapper).collect();
values.sort();
assert_eq!(values, expected_sorted_values);
}

View File

@@ -6,7 +6,6 @@ authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Axum integrations for the Leptos web framework."
rust-version.workspace = true
[dependencies]
axum = { version = "0.7", default-features = false, features = [
@@ -15,7 +14,7 @@ axum = { version = "0.7", default-features = false, features = [
futures = "0.3"
http-body-util = "0.1"
leptos = { workspace = true, features = ["ssr"] }
server_fn = { workspace = true, features = ["axum-no-default"] }
server_fn = { workspace = true, features = ["axum"] }
leptos_macro = { workspace = true, features = ["axum"] }
leptos_meta = { workspace = true, features = ["ssr"] }
leptos_router = { workspace = true, features = ["ssr"] }

View File

@@ -54,7 +54,7 @@ use leptos_meta::{generate_head_metadata_separated, MetaContext};
use leptos_router::*;
use once_cell::sync::OnceCell;
use parking_lot::RwLock;
use server_fn::{error::NoCustomError, redirect::REDIRECT_HEADER};
use server_fn::redirect::REDIRECT_HEADER;
use std::{fmt::Debug, io, pin::Pin, sync::Arc, thread::available_parallelism};
use tokio_util::task::LocalPoolHandle;
use tracing::Instrument;
@@ -79,20 +79,6 @@ impl ResponseParts {
}
/// Allows you to override details of the HTTP response like the status code and add Headers/Cookies.
///
/// `ResponseOptions` is provided via context when you use most of the handlers provided in this
/// crate, including [`.leptos_routes`](LeptosRoutes::leptos_routes),
/// [`.leptos_routes_with_context`](LeptosRoutes::leptos_routes_with_context), [`handle_server_fns`], etc.
/// You can find the full set of provided context types in each handler function.
///
/// If you provide your own handler, you will need to provide `ResponseOptions` via context
/// yourself if you want to access it via context.
/// ```rust,ignore
/// #[server]
/// pub async fn get_opts() -> Result<(), ServerFnError> {
/// let opts = expect_context::<leptos_axum::ResponseOptions>();
/// Ok(())
/// }
#[derive(Debug, Clone, Default)]
pub struct ResponseOptions(pub Arc<RwLock<ResponseParts>>);
@@ -271,13 +257,7 @@ async fn handle_server_fns_inner(
let (tx, rx) = futures::channel::oneshot::channel();
// capture current span to enable trace context propagation
let current_span = tracing::Span::current();
spawn_task!(async move {
// enter captured span for trace context propagation in spawned task
let _guard = current_span.enter();
let path = req.uri().path().to_string();
let (req, parts) = generate_request_and_parts(req);
@@ -349,15 +329,7 @@ async fn handle_server_fns_inner(
_ = tx.send(res);
});
rx.await.unwrap_or_else(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
ServerFnError::<NoCustomError>::ServerError(e.to_string())
.ser()
.unwrap_or_default(),
)
.into_response()
})
rx.await.unwrap()
}
pub type PinnedHtmlStream =
@@ -1614,14 +1586,6 @@ where
where
IV: IntoView + 'static,
{
// S represents the router's finished state allowing us to provide
// it to the user's server functions.
let state = options.clone();
let cx_with_state = move || {
provide_context::<S>(state.clone());
additional_context();
};
let mut router = self;
// register router paths
@@ -1629,11 +1593,6 @@ where
let path = listing.path();
for method in listing.methods() {
let cx_with_state = cx_with_state.clone();
let cx_with_state_and_method = move || {
provide_context(method);
cx_with_state();
};
router = if let Some(static_mode) = listing.static_mode() {
#[cfg(feature = "default")]
{
@@ -1642,7 +1601,7 @@ where
path,
LeptosOptions::from_ref(options),
app_fn.clone(),
cx_with_state_and_method.clone(),
additional_context.clone(),
method,
static_mode,
)
@@ -1662,7 +1621,7 @@ where
SsrMode::OutOfOrder => {
let s = render_app_to_stream_with_context(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
);
match method {
@@ -1676,7 +1635,7 @@ where
SsrMode::PartiallyBlocked => {
let s = render_app_to_stream_with_context_and_replace_blocks(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
true
);
@@ -1691,7 +1650,7 @@ where
SsrMode::InOrder => {
let s = render_app_to_stream_in_order_with_context(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
);
match method {
@@ -1705,7 +1664,7 @@ where
SsrMode::Async => {
let s = render_app_async_with_context(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
additional_context.clone(),
app_fn.clone(),
);
match method {
@@ -1724,9 +1683,9 @@ where
// register server functions
for (path, method) in server_fn::axum::server_fn_paths() {
let cx_with_state = cx_with_state.clone();
let additional_context = additional_context.clone();
let handler = move |req: Request<Body>| async move {
handle_server_fns_with_context(cx_with_state, req).await
handle_server_fns_with_context(additional_context, req).await
};
router = router.route(
path,

View File

@@ -6,7 +6,6 @@ authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Utilities to help build server integrations for the Leptos web framework."
rust-version.workspace = true
[dependencies]
futures = "0.3"

View File

@@ -2,7 +2,6 @@ use futures::{Stream, StreamExt};
use leptos::{nonce::use_nonce, use_context, RuntimeId};
use leptos_config::LeptosOptions;
use leptos_meta::MetaContext;
use std::{borrow::Cow, collections::HashMap, env, fs};
extern crate tracing;
@@ -56,9 +55,7 @@ pub fn html_parts_separated(
options: &LeptosOptions,
meta: Option<&MetaContext>,
) -> (String, &'static str) {
let pkg_path = option_env!("CDN_PKG_PATH")
.map(Cow::from)
.unwrap_or_else(|| format!("/{}", options.site_pkg_dir).into());
let pkg_path = &options.site_pkg_dir;
let output_name = &options.output_name;
let nonce = use_nonce();
let nonce = nonce
@@ -103,14 +100,6 @@ pub fn html_parts_separated(
} else {
"() => mod.hydrate()"
};
let (js_hash, wasm_hash, css_hash) = get_hashes(options);
let head = head.replace(
&format!("{output_name}.css"),
&format!("{output_name}{css_hash}.css"),
);
let head = format!(
r#"<!DOCTYPE html>
<html{html_metadata}>
@@ -118,8 +107,8 @@ pub fn html_parts_separated(
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
{head}
<link rel="modulepreload" href="{pkg_path}/{output_name}{js_hash}.js"{nonce}>
<link rel="preload" href="{pkg_path}/{wasm_output_name}{wasm_hash}.wasm" as="fetch" type="application/wasm" crossorigin=""{nonce}>
<link rel="modulepreload" href="/{pkg_path}/{output_name}.js"{nonce}>
<link rel="preload" href="/{pkg_path}/{wasm_output_name}.wasm" as="fetch" type="application/wasm" crossorigin=""{nonce}>
<script type="module"{nonce}>
function idle(c) {{
if ("requestIdleCallback" in window) {{
@@ -129,9 +118,9 @@ pub fn html_parts_separated(
}}
}}
idle(() => {{
import('{pkg_path}/{output_name}{js_hash}.js')
import('/{pkg_path}/{output_name}.js')
.then(mod => {{
mod.default('{pkg_path}/{wasm_output_name}{wasm_hash}.wasm').then({import_callback});
mod.default('/{pkg_path}/{wasm_output_name}.wasm').then({import_callback});
}})
}});
</script>
@@ -142,46 +131,6 @@ pub fn html_parts_separated(
(head, tail)
}
#[tracing::instrument(level = "trace", fields(error), skip_all)]
fn get_hashes(options: &LeptosOptions) -> (String, String, String) {
let mut ext_to_hash = HashMap::from([
("js".to_string(), "".to_string()),
("wasm".to_string(), "".to_string()),
("css".to_string(), "".to_string()),
]);
if options.hash_files {
let hash_path = env::current_exe()
.map(|path| {
path.parent().map(|p| p.to_path_buf()).unwrap_or_default()
})
.unwrap_or_default()
.join(&options.hash_file);
if hash_path.exists() {
let hashes = fs::read_to_string(&hash_path)
.expect("failed to read hash file");
for line in hashes.lines() {
let line = line.trim();
if !line.is_empty() {
if let Some((k, v)) = line.split_once(':') {
ext_to_hash.insert(
k.trim().to_string(),
format!(".{}", v.trim()),
);
}
}
}
}
}
(
ext_to_hash["js"].clone(),
ext_to_hash["wasm"].clone(),
ext_to_hash["css"].clone(),
)
}
#[tracing::instrument(level = "trace", fields(error), skip_all)]
pub async fn build_async_response(
stream: impl Stream<Item = String> + 'static,

View File

@@ -7,7 +7,6 @@ license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces."
readme = "../README.md"
rust-version.workspace = true
[dependencies]
cfg-if = "1"
@@ -16,7 +15,6 @@ leptos_macro = { workspace = true }
leptos_reactive = { workspace = true }
leptos_server = { workspace = true }
leptos_config = { workspace = true }
leptos-spin-macro = { version="0.1", optional = true}
tracing = "0.1"
typed-builder = "0.18"
typed-builder-macro = "0.18"
@@ -68,10 +66,7 @@ miniserde = ["leptos_reactive/miniserde"]
rkyv = ["leptos_reactive/rkyv"]
tracing = ["leptos_macro/tracing"]
nonce = ["leptos_dom/nonce"]
spin = [
"leptos_reactive/spin",
"leptos-spin-macro"
]
spin = ["leptos_reactive/spin"]
experimental-islands = [
"leptos_dom/experimental-islands",
"leptos_macro/experimental-islands",
@@ -92,9 +87,7 @@ denylist = [
"rustls",
"default-tls",
"wasm-bindgen",
"trace-component-props",
"spin",
"experimental-islands"
"trace-component-props"
]
skip_feature_sets = [
[

View File

@@ -179,14 +179,7 @@ pub mod error {
pub use leptos_macro::template;
#[cfg(not(all(target_arch = "wasm32", feature = "template_macro")))]
pub use leptos_macro::view as template;
pub use leptos_macro::{component, island, slice, slot, view, Params};
cfg_if::cfg_if!(
if #[cfg(feature="spin")] {
pub use leptos_spin_macro::server;
} else {
pub use leptos_macro::server;
}
);
pub use leptos_macro::{component, island, server, slice, slot, view, Params};
pub use leptos_reactive::*;
pub use leptos_server::{
self, create_action, create_multi_action, create_server_action,

View File

@@ -36,7 +36,7 @@ use std::rc::Rc;
/// <div>
/// <Suspense fallback=move || view! { <p>"Loading (Suspense Fallback)..."</p> }>
/// {move || {
/// cats.get().map(|data| match data {
/// cats.read().map(|data| match data {
/// None => view! { <pre>"Error"</pre> }.into_view(),
/// Some(cats) => cats
/// .iter()
@@ -173,9 +173,6 @@ where
runtime,
);
#[cfg(feature = "experimental-islands")]
let prev_no_hydrate =
SharedContext::no_hydrate();
#[cfg(feature = "experimental-islands")]
{
SharedContext::set_no_hydrate(
@@ -183,7 +180,7 @@ where
);
}
let rendered = with_owner(owner, {
with_owner(owner, {
move || {
HydrationCtx::continue_from(
current_id,
@@ -197,15 +194,7 @@ where
.render_to_string()
.to_string()
}
});
#[cfg(feature = "experimental-islands")]
SharedContext::set_no_hydrate(
prev_no_hydrate,
);
#[allow(clippy::let_and_return)]
rendered
})
}
},
// in-order streaming
@@ -216,9 +205,6 @@ where
runtime,
);
#[cfg(feature = "experimental-islands")]
let prev_no_hydrate =
SharedContext::no_hydrate();
#[cfg(feature = "experimental-islands")]
{
SharedContext::set_no_hydrate(
@@ -226,7 +212,7 @@ where
);
}
let rendered = with_owner(owner, {
with_owner(owner, {
move || {
HydrationCtx::continue_from(
current_id,
@@ -239,15 +225,7 @@ where
.into_view()
.into_stream_chunks()
}
});
#[cfg(feature = "experimental-islands")]
SharedContext::set_no_hydrate(
prev_no_hydrate,
);
#[allow(clippy::let_and_return)]
rendered
})
}
},
);

View File

@@ -155,9 +155,7 @@ fn is_first_run(
first_run: RwSignal<bool>,
suspense_context: &SuspenseContext,
) -> bool {
if cfg!(feature = "csr")
|| (cfg!(feature = "hydrate") && !HydrationCtx::is_hydrating())
{
if cfg!(feature = "csr") {
false
} else {
match (

View File

@@ -206,5 +206,3 @@ fn ssr_option() {
runtime.dispose();
}
// TODO: remove simulated change

View File

@@ -7,10 +7,9 @@ license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Configuration for the Leptos web framework."
readme = "../README.md"
rust-version.workspace = true
[dependencies]
config = { version = "0.14", default-features = false, features = ["toml"] }
config = { version = "0.13.3", default-features = false, features = ["toml"] }
regex = "1.7.0"
serde = { version = "1.0.151", features = ["derive"] }
thiserror = "1.0.38"

View File

@@ -1,4 +1,4 @@
use std::{net::AddrParseError, num::ParseIntError, str::ParseBoolError};
use std::{net::AddrParseError, num::ParseIntError};
use thiserror::Error;
#[derive(Debug, Error, Clone)]
@@ -31,9 +31,3 @@ impl From<AddrParseError> for LeptosConfigError {
Self::ConfigError(e.to_string())
}
}
impl From<ParseBoolError> for LeptosConfigError {
fn from(e: ParseBoolError) -> Self {
Self::ConfigError(e.to_string())
}
}

Some files were not shown because too many files have changed in this diff Show More