Compare commits

..

40 Commits

Author SHA1 Message Date
Greg Johnston
d665dd4b89 v0.7.0 2024-11-30 12:09:41 -05:00
Greg Johnston
be740b38ee feat: add NodeRef::on_load() and writeable NodeRef (#3305) 2024-11-30 11:55:13 -05:00
Rakshith Ravi
d2803c938c chore: remove pre-release message from README (#3075) 2024-11-30 11:48:50 -05:00
Greg Johnston
f29224415a fix: sort tuple-syntax class and style in addition to colon-syntax (closes #3296) (#3303) 2024-11-29 19:28:11 -05:00
Greg Johnston
292772c4d6 fix: allow a deprecated wasm-bindgen struct 2024-11-29 15:50:57 -05:00
benwis
5947aa299e Release rc3 2024-11-28 11:28:20 -08:00
Greg Johnston
6098836cf7 docs: improve line location of hydration error message when using view macro (#3293) 2024-11-27 14:36:31 -05:00
Niklas Eicker
2dfa61ff6a docs: fix help message for island macro (#3287) 2024-11-24 14:45:10 -05:00
Darwin Boersma
4f39b0b0ef Version any_spawner alongside other crates, reexport CustomExecutor (#3284)
* rc2 version any_spawner

Signed-off-by: Darwin Boersma <darwin@sadlark.com>

* reexport full any_spawner crate

Signed-off-by: Darwin Boersma <darwin@sadlark.com>

---------

Signed-off-by: Darwin Boersma <darwin@sadlark.com>
2024-11-23 14:49:43 -08:00
dependabot[bot]
980595f1f0 chore(deps): bump proc-macro2 from 1.0.91 to 1.0.92 (#3276)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.91 to 1.0.92.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.91...1.0.92)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-23 08:29:52 -05:00
Greg Johnston
14eb707e82 rc2 2024-11-22 15:27:00 -05:00
Greg Johnston
3de0414ed5 docs: adds more helpful text for hydration errors (closes #3267) (#3275) 2024-11-22 15:10:37 -05:00
Greg Johnston
75cae91661 chore: expose some internals of routing to make wrappers easier (#3232) 2024-11-22 15:10:23 -05:00
Greg Johnston
8b258b0d26 fix: notify Suspense on subsequent reloads (closes #3277) (#3278) 2024-11-22 14:55:24 -05:00
dependabot[bot]
84bdd6b568 chore(deps): bump serde_json from 1.0.132 to 1.0.133 (#3261)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.132 to 1.0.133.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.132...v1.0.133)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 19:24:47 -05:00
dependabot[bot]
809023a2ad chore(deps): bump attribute-derive from 0.10.2 to 0.10.3 (#3260)
Bumps [attribute-derive](https://github.com/ModProg/attribute-derive) from 0.10.2 to 0.10.3.
- [Release notes](https://github.com/ModProg/attribute-derive/releases)
- [Changelog](https://github.com/ModProg/attribute-derive/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ModProg/attribute-derive/commits)

---
updated-dependencies:
- dependency-name: attribute-derive
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 19:24:39 -05:00
dependabot[bot]
1477ae2cfb chore(deps): bump axum from 0.7.8 to 0.7.9 (#3259)
Bumps [axum](https://github.com/tokio-rs/axum) from 0.7.8 to 0.7.9.
- [Release notes](https://github.com/tokio-rs/axum/releases)
- [Changelog](https://github.com/tokio-rs/axum/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/axum/compare/axum-v0.7.8...axum-v0.7.9)

---
updated-dependencies:
- dependency-name: axum
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 19:24:31 -05:00
dependabot[bot]
26995f8efd chore(deps): bump tower-http from 0.6.1 to 0.6.2 (#3266)
Bumps [tower-http](https://github.com/tower-rs/tower-http) from 0.6.1 to 0.6.2.
- [Release notes](https://github.com/tower-rs/tower-http/releases)
- [Commits](https://github.com/tower-rs/tower-http/compare/tower-http-0.6.1...tower-http-0.6.2)

---
updated-dependencies:
- dependency-name: tower-http
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 19:24:23 -05:00
dependabot[bot]
3c174b26a5 chore(deps): bump syn from 2.0.87 to 2.0.89 (#3272)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.87 to 2.0.89.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.87...2.0.89)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 19:24:13 -05:00
dependabot[bot]
0c7e77800a chore(deps): bump proc-macro2 from 1.0.89 to 1.0.91 (#3273)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.89 to 1.0.91.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.89...1.0.91)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 19:24:04 -05:00
Greg Johnston
fee2421047 chore: fix broken links, etc. in docs (#3269) 2024-11-21 19:23:55 -05:00
Ari Seyhun
89f26f6e9b feat: add fallback prop to ProtectedRoute and ProtectedParentRoute (#3264) 2024-11-19 22:32:45 -08:00
Silver
e76b22bec8 Remove unused shell() function definition (#3254) 2024-11-19 12:17:45 -08:00
Greg Johnston
cff277b3db fix: add Write implementatations for Field<T> and ArcField<T> (closes #3257) (#3262) 2024-11-19 10:04:40 -05:00
Ari Seyhun
0258ac6df4 feat(reactive_stores): add map_untracked to OptionStoreExt (#3245) 2024-11-18 09:41:04 -05:00
Greg Johnston
36132a5823 Merge pull request #3249 from metatoaster/pr3213_e2e_tests
test: aria-current end-to-end tests (#3213)
2024-11-17 15:50:13 -05:00
autofix-ci[bot]
6f35f8d197 [autofix.ci] apply automated fixes 2024-11-17 01:51:40 +00:00
Tommy Yu
3973c4f420 test: aria-current end-to-end tests (#3213) 2024-11-17 14:40:47 +13:00
benwis
7f2237b91e Remove hash.txt from tracking, update dependencies in tests 2024-11-16 11:19:11 -08:00
Tommy Yu
aa6cd08387 test: fixtures for testing aria-current (#3202) 2024-11-16 11:11:54 -08:00
dependabot[bot]
15fc7dd7be chore(deps): bump thiserror from 2.0.0 to 2.0.3 (#3231)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.0 to 2.0.3.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.0...2.0.3)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-16 11:07:00 -08:00
dependabot[bot]
eac979e309 chore(deps): bump serde from 1.0.214 to 1.0.215 (#3236)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.214 to 1.0.215.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.214...v1.0.215)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-16 11:06:49 -08:00
dependabot[bot]
f7ac7be32b chore(deps): bump glib from 0.20.5 to 0.20.6 (#3237)
Bumps [glib](https://github.com/gtk-rs/gtk-rs-core) from 0.20.5 to 0.20.6.
- [Release notes](https://github.com/gtk-rs/gtk-rs-core/releases)
- [Changelog](https://github.com/gtk-rs/gtk-rs-core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gtk-rs/gtk-rs-core/compare/0.20.5...0.20.6)

---
updated-dependencies:
- dependency-name: glib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-16 11:06:39 -08:00
dependabot[bot]
2462a1dc92 chore(deps): bump axum from 0.7.7 to 0.7.8 (#3246)
Bumps [axum](https://github.com/tokio-rs/axum) from 0.7.7 to 0.7.8.
- [Release notes](https://github.com/tokio-rs/axum/releases)
- [Changelog](https://github.com/tokio-rs/axum/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/axum/compare/axum-v0.7.7...axum-v0.7.8)

---
updated-dependencies:
- dependency-name: axum
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-16 11:06:30 -08:00
Greg Johnston
2c6d790cdb change: rename from experimental-islands to islands (#3247)
* change: rename from `experimental-islands` to `islands`

* update examples
2024-11-16 11:06:18 -08:00
Kalin Staykov
1c26261fd7 fix: migration syntax and insert query (#2597) 2024-11-13 09:37:19 -08:00
Saber Haj Rabiee
c2b239dba2 fix: run autofix.ci on pull requests only (#3238) 2024-11-12 14:16:34 -08:00
Saber Haj Rabiee
d4044cd5a1 feat: add autofix.ci to address formatting issues and possible clippy fixes (#3235)
* feat: add `autofix.ci` to address formatting issues and possible clippy
fixes

* fix: initial run of `autofix.ci` script to prevent PR pollution at first
run

* fix: typo and indent issue in `autofix.yml`

* fix: run `autofix.ci` over members with no features
2024-11-12 10:54:14 -08:00
Greg Johnston
43912f4fd0 chore: fmt 2024-11-12 09:05:36 -05:00
Greg Johnston
a5293f0b79 chore: add missing docs for 0.7 (#3203) 2024-11-11 19:58:38 -05:00
83 changed files with 6332 additions and 425 deletions

49
.github/workflows/autofix.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: autofix.ci
on:
pull_request:
# Running this workflow on main branch pushes requires write permission to apply changes.
# Leave it alone for future uses.
# push:
# branches: ["main"]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
autofix:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with: {toolchain: nightly, components: "rustfmt, clippy", target: "wasm32-unknown-unknown", rustflags: ""}
- name: Install jq
run: sudo apt-get install jq
- run: |
echo "Formatting the workspace"
cargo fmt --all
echo "Running Clippy against each member's features (default features included)"
for member in $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | .name'); do
echo "Working on member $member":
echo -e "\tdefault-features/no-features:"
# this will also run on members with no features or default features
cargo clippy --allow-dirty --fix --lib --package "$member"
features=$(cargo metadata --no-deps --format-version 1 | jq -r ".packages[] | select(.name == \"$member\") | .features | keys[]")
for feature in $features; do
if [ "$feature" = "default" ]; then
continue
fi
echo -e "\tfeature $feature"
cargo clippy --allow-dirty --fix --lib --package "$member" --features "$feature"
done
done
- uses: autofix-ci/action@v1.3.1
if: ${{ always() }}
with:
fail-fast: false

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ blob.rs
.vscode
vendor
hash.txt

218
Cargo.lock generated
View File

@@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -205,7 +205,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -273,12 +273,12 @@ dependencies = [
[[package]]
name = "any_spawner"
version = "0.1.1"
version = "0.2.0"
dependencies = [
"async-executor",
"futures",
"glib",
"thiserror 2.0.0",
"thiserror 2.0.3",
"tokio",
"tracing",
"wasm-bindgen-futures",
@@ -333,7 +333,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -350,7 +350,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -364,23 +364,23 @@ dependencies = [
[[package]]
name = "attribute-derive"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1800e974930e9079c965b9ffbcb6667a40401063a26396c7b4f15edc92da690"
checksum = "0053e96dd3bec5b4879c23a138d6ef26f2cb936c9cdc96274ac2b9ed44b5bb54"
dependencies = [
"attribute-derive-macro",
"derive-where",
"manyhow",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
name = "attribute-derive-macro"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d908eb786ef94296bff86f90130b3b748b49401dc81fd2bb8b3dccd44cfacbd"
checksum = "463b53ad0fd5b460af4b1915fe045ff4d946d025fb6c4dc3337752eaa980f71b"
dependencies = [
"collection_literals",
"interpolator",
@@ -389,7 +389,7 @@ dependencies = [
"proc-macro2",
"quote",
"quote-use",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -400,9 +400,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.7.7"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
dependencies = [
"async-trait",
"axum-core",
@@ -571,7 +571,7 @@ checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -867,7 +867,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -880,7 +880,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -901,7 +901,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -1109,7 +1109,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -1171,9 +1171,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gio-sys"
version = "0.20.5"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "217f464cad5946ae4369c355155e2d16b488c08920601083cb4891e352ae777b"
checksum = "b965df6f3534c84816b5c1a7d9efcb5671ae790822de5abe8e299797039529bc"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -1184,9 +1184,9 @@ dependencies = [
[[package]]
name = "glib"
version = "0.20.5"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "358431b0e0eb15b9d02db52e1f19c805b953c5c168099deb3de88beab761768c"
checksum = "86bd3e4ee7998ab5a135d900db56930cc19ad16681adf245daff54f618b9d5e1"
dependencies = [
"bitflags",
"futures-channel",
@@ -1213,14 +1213,14 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
name = "glib-sys"
version = "0.20.5"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a5911863ab7ecd4a6f8d5976f12eeba076b23669c49b066d877e742544aa389"
checksum = "3d0b1827e8621fc42c0dfb228e5d57ff6a71f9699e666ece8113f979ad87c2de"
dependencies = [
"libc",
"system-deps",
@@ -1448,7 +1448,7 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hydration_context"
version = "0.2.0-rc1"
version = "0.2.0"
dependencies = [
"futures",
"js-sys",
@@ -1648,7 +1648,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -1765,7 +1765,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leptos"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"any_spawner",
"base64",
@@ -1792,7 +1792,7 @@ dependencies = [
"server_fn",
"slotmap",
"tachys",
"thiserror 2.0.0",
"thiserror 2.0.3",
"throw_error",
"tracing",
"typed-builder",
@@ -1810,12 +1810,12 @@ dependencies = [
"http 1.1.0",
"proc-macro-error",
"server_fn_macro 0.6.15",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
name = "leptos_actix"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"actix-files",
"actix-http",
@@ -1840,7 +1840,7 @@ dependencies = [
[[package]]
name = "leptos_axum"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"any_spawner",
"axum",
@@ -1863,21 +1863,21 @@ dependencies = [
[[package]]
name = "leptos_config"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"config",
"regex",
"serde",
"temp-env",
"tempfile",
"thiserror 2.0.0",
"thiserror 2.0.3",
"tokio",
"typed-builder",
]
[[package]]
name = "leptos_dom"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"js-sys",
"leptos",
@@ -1894,7 +1894,7 @@ dependencies = [
[[package]]
name = "leptos_hot_reload"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"anyhow",
"camino",
@@ -1904,13 +1904,13 @@ dependencies = [
"quote",
"rstml",
"serde",
"syn 2.0.87",
"syn 2.0.89",
"walkdir",
]
[[package]]
name = "leptos_integration_utils"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"futures",
"hydration_context",
@@ -1923,7 +1923,7 @@ dependencies = [
[[package]]
name = "leptos_macro"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"attribute-derive",
"cfg-if",
@@ -1941,8 +1941,8 @@ dependencies = [
"rstml",
"serde",
"server_fn",
"server_fn_macro 0.7.0-rc1",
"syn 2.0.87",
"server_fn_macro 0.7.0",
"syn 2.0.89",
"tracing",
"trybuild",
"typed-builder",
@@ -1951,7 +1951,7 @@ dependencies = [
[[package]]
name = "leptos_meta"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"futures",
"indexmap",
@@ -1966,7 +1966,7 @@ dependencies = [
[[package]]
name = "leptos_router"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"any_spawner",
"either_of",
@@ -1981,7 +1981,7 @@ dependencies = [
"reactive_graph",
"send_wrapper",
"tachys",
"thiserror 2.0.0",
"thiserror 2.0.3",
"tracing",
"url",
"wasm-bindgen",
@@ -1990,7 +1990,7 @@ dependencies = [
[[package]]
name = "leptos_router_macro"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"leptos_router",
"proc-macro-error2",
@@ -2000,7 +2000,7 @@ dependencies = [
[[package]]
name = "leptos_server"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"any_spawner",
"base64",
@@ -2091,7 +2091,7 @@ dependencies = [
"manyhow-macros",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2141,7 +2141,7 @@ checksum = "f3cd9f9bbedc1b92683a9847b8db12f3203cf32af6a11db085fa007708dc9555"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2217,7 +2217,7 @@ checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2239,7 +2239,7 @@ dependencies = [
[[package]]
name = "next_tuple"
version = "0.1.0-rc1"
version = "0.1.0"
[[package]]
name = "nom"
@@ -2291,7 +2291,7 @@ version = "0.2.0"
dependencies = [
"serde",
"serde_json",
"thiserror 2.0.0",
"thiserror 2.0.3",
]
[[package]]
@@ -2323,7 +2323,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2412,7 +2412,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2468,7 +2468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [
"proc-macro2",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2523,7 +2523,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2539,9 +2539,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.89"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
@@ -2554,7 +2554,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"version_check",
"yansi",
]
@@ -2596,7 +2596,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2676,7 +2676,7 @@ dependencies = [
"proc-macro-utils",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2726,7 +2726,7 @@ dependencies = [
[[package]]
name = "reactive_graph"
version = "0.1.0-rc1"
version = "0.1.0"
dependencies = [
"any_spawner",
"async-lock",
@@ -2739,7 +2739,7 @@ dependencies = [
"send_wrapper",
"serde",
"slotmap",
"thiserror 2.0.0",
"thiserror 2.0.3",
"tokio",
"tokio-test",
"tracing",
@@ -2748,7 +2748,7 @@ dependencies = [
[[package]]
name = "reactive_stores"
version = "0.1.0-rc1"
version = "0.1.0"
dependencies = [
"any_spawner",
"guardian",
@@ -2764,13 +2764,13 @@ dependencies = [
[[package]]
name = "reactive_stores_macro"
version = "0.1.0-rc1"
version = "0.1.0"
dependencies = [
"convert_case 0.6.0",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2954,7 +2954,7 @@ checksum = "09cb82b74b4810f07e460852c32f522e979787691b0b7b7439fe473e49d49b2f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -2989,7 +2989,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"syn_derive",
"thiserror 1.0.68",
]
@@ -3156,9 +3156,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.214"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
@@ -3181,7 +3181,7 @@ checksum = "7ce26a84e3d8d10853301cf6a75c58132b8f5d5e8fee65949ea8dd7758d6760b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -3197,20 +3197,20 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.214"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
name = "serde_json"
version = "1.0.132"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"itoa",
"memchr",
@@ -3262,7 +3262,7 @@ dependencies = [
[[package]]
name = "server_fn"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"actix-web",
"axum",
@@ -3290,7 +3290,7 @@ dependencies = [
"serde_json",
"serde_qs",
"server_fn_macro_default",
"thiserror 2.0.0",
"thiserror 2.0.3",
"throw_error",
"tower",
"tower-layer",
@@ -3312,28 +3312,28 @@ dependencies = [
"convert_case 0.6.0",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"xxhash-rust",
]
[[package]]
name = "server_fn_macro"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"const_format",
"convert_case 0.6.0",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"xxhash-rust",
]
[[package]]
name = "server_fn_macro_default"
version = "0.7.0-rc1"
version = "0.7.0"
dependencies = [
"server_fn_macro 0.7.0-rc1",
"syn 2.0.87",
"server_fn_macro 0.7.0",
"syn 2.0.89",
]
[[package]]
@@ -3400,7 +3400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33a1b4f13e2bbf2f5b29d09dfebc9de69229ffee245aed80e3b70f9b5fd28c06"
dependencies = [
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -3471,9 +3471,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.87"
version = "2.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
dependencies = [
"proc-macro2",
"quote",
@@ -3489,7 +3489,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -3515,7 +3515,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -3533,7 +3533,7 @@ dependencies = [
[[package]]
name = "tachys"
version = "0.1.0-rc1"
version = "0.1.0"
dependencies = [
"any_spawner",
"const_str_slice_concat",
@@ -3627,11 +3627,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.0"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15291287e9bff1bc6f9ff3409ed9af665bec7a5fc8ac079ea96be07bca0e2668"
checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
dependencies = [
"thiserror-impl 2.0.0",
"thiserror-impl 2.0.3",
]
[[package]]
@@ -3642,23 +3642,23 @@ checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
name = "thiserror-impl"
version = "2.0.0"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972"
checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
name = "throw_error"
version = "0.2.0-rc1"
version = "0.2.0"
dependencies = [
"pin-project-lite",
]
@@ -3745,7 +3745,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -3858,9 +3858,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
dependencies = [
"bitflags",
"bytes",
@@ -3913,7 +3913,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -3963,7 +3963,7 @@ checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -4111,7 +4111,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"wasm-bindgen-shared",
]
@@ -4145,7 +4145,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -4371,7 +4371,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"synstructure",
]
@@ -4393,7 +4393,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]
@@ -4413,7 +4413,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
"synstructure",
]
@@ -4442,7 +4442,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"syn 2.0.89",
]
[[package]]

View File

@@ -40,36 +40,36 @@ members = [
exclude = ["benchmarks", "examples", "projects"]
[workspace.package]
version = "0.7.0-rc1"
version = "0.7.0"
edition = "2021"
rust-version = "1.76"
[workspace.dependencies]
throw_error = { path = "./any_error/", version = "0.2.0-rc1" }
any_spawner = { path = "./any_spawner/", version = "0.1.0" }
throw_error = { path = "./any_error/", version = "0.2.0" }
any_spawner = { path = "./any_spawner/", version = "0.2.0" }
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1.0" }
either_of = { path = "./either_of/", version = "0.1.0" }
hydration_context = { path = "./hydration_context", version = "0.2.0-rc1" }
leptos = { path = "./leptos", version = "0.7.0-rc1" }
leptos_config = { path = "./leptos_config", version = "0.7.0-rc1" }
leptos_dom = { path = "./leptos_dom", version = "0.7.0-rc1" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0-rc1" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0-rc1" }
leptos_macro = { path = "./leptos_macro", version = "0.7.0-rc1" }
leptos_router = { path = "./router", version = "0.7.0-rc1" }
leptos_router_macro = { path = "./router_macro", version = "0.7.0-rc1" }
leptos_server = { path = "./leptos_server", version = "0.7.0-rc1" }
leptos_meta = { path = "./meta", version = "0.7.0-rc1" }
next_tuple = { path = "./next_tuple", version = "0.1.0-rc1" }
hydration_context = { path = "./hydration_context", version = "0.2.0" }
leptos = { path = "./leptos", version = "0.7.0" }
leptos_config = { path = "./leptos_config", version = "0.7.0" }
leptos_dom = { path = "./leptos_dom", version = "0.7.0" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0" }
leptos_macro = { path = "./leptos_macro", version = "0.7.0" }
leptos_router = { path = "./router", version = "0.7.0" }
leptos_router_macro = { path = "./router_macro", version = "0.7.0" }
leptos_server = { path = "./leptos_server", version = "0.7.0" }
leptos_meta = { path = "./meta", version = "0.7.0" }
next_tuple = { path = "./next_tuple", version = "0.1.0" }
oco_ref = { path = "./oco", version = "0.2.0" }
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
reactive_graph = { path = "./reactive_graph", version = "0.1.0-rc1" }
reactive_stores = { path = "./reactive_stores", version = "0.1.0-rc1" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0-rc1" }
server_fn = { path = "./server_fn", version = "0.7.0-rc1" }
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0-rc1" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0-rc1" }
tachys = { path = "./tachys", version = "0.1.0-rc1" }
reactive_graph = { path = "./reactive_graph", version = "0.1.0" }
reactive_stores = { path = "./reactive_stores", version = "0.1.0" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0" }
server_fn = { path = "./server_fn", version = "0.7.0" }
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0" }
tachys = { path = "./tachys", version = "0.1.0" }
[profile.release]
codegen-units = 1

View File

@@ -12,8 +12,6 @@
You can find a list of useful libraries and example projects at [`awesome-leptos`](https://github.com/leptos-rs/awesome-leptos).
# The `main` branch is currently undergoing major changes in preparation for the [0.7](https://github.com/leptos-rs/leptos/milestone/4) release. For a stable version, please use the [v0.6.13 tag](https://github.com/leptos-rs/leptos/tree/v0.6.13)
# Leptos
```rust

View File

@@ -1,6 +1,6 @@
[package]
name = "throw_error"
version = "0.2.0-rc1"
version = "0.2.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -1,6 +1,6 @@
[package]
name = "any_spawner"
version = "0.1.1"
version = "0.2.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"
@@ -11,7 +11,7 @@ edition.workspace = true
[dependencies]
async-executor = { version = "1.13.1", optional = true }
futures = "0.3.31"
glib = { version = "0.20.5", optional = true }
glib = { version = "0.20.6", optional = true }
thiserror = "2.0"
tokio = { version = "1.41", optional = true, default-features = false, features = [
"rt",

View File

@@ -12,7 +12,7 @@ lto = true
[dependencies]
console_error_panic_hook = "0.1.7"
leptos = { path = "../../leptos", features = ["experimental-islands"] }
leptos = { path = "../../leptos", features = ["islands"] }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta" }
leptos_router = { path = "../../router" }

View File

@@ -12,7 +12,7 @@ futures = "0.3.30"
http = "1.1"
leptos = { path = "../../leptos", features = [
"tracing",
"experimental-islands",
"islands",
] }
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
leptos_axum = { path = "../../integrations/axum", optional = true }

View File

@@ -12,7 +12,7 @@ futures = "0.3.30"
http = "1.1"
leptos = { path = "../../leptos", features = [
"tracing",
"experimental-islands",
"islands",
] }
leptos_router = { path = "../../router" }
server_fn = { path = "../../server_fn", features = ["serde-lite"] }

View File

@@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
actix-files = { version = "0.6.6", optional = true }
actix-web = { version = "4.8", optional = true, features = ["macros"] }
console_error_panic_hook = "0.1.7"
js-sys = { version = "0.3.70", optional = true }
js-sys = { version = "0.3.72" }
leptos = { path = "../../leptos" }
leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_router = { path = "../../router" }
@@ -21,7 +21,7 @@ tokio = { version = "1.39", features = ["time", "rt"], optional = true }
[features]
hydrate = [
"dep:js-sys",
"leptos/hydrate",
]
ssr = [

View File

@@ -0,0 +1,121 @@
@check_aria_current
Feature: Check aria-current being applied to make links bolded
Background:
Given I see the app
Scenario: Should see the base case working
Then I see the Out-of-Order link being bolded
And I see the following links being bolded
| Out-of-Order |
| Nested |
And I see the In-Order link not being bolded
And I see the following links not being bolded
| In-Order |
| Single |
Scenario: Should see client-side render the correct bolded links
When I select the link In-Order
And I select the link Single
Then I see the following links being bolded
| In-Order |
| Single |
And I see the following links not being bolded
| Out-of-Order |
| Nested |
Scenario: Should see server-side render the correct bolded links
When I select the link In-Order
And I select the link Single
And I reload the page
Then I see the following links being bolded
| In-Order |
| Single |
And I see the following links not being bolded
| Out-of-Order |
| Nested |
Scenario: Check that the base nested route links are working
When I select the link Instrumented
Then I see the Instrumented link being bolded
And I see the Item Listing link not being bolded
Scenario: Should see going deep down into nested routes bold links
When I select the link Instrumented
And I select the link Target 421
Then I see the following links being bolded
| Instrumented |
| Item Listing |
| Target 4## |
| Target 42# |
| Target 421 |
| field1 |
Scenario: Should see going deep down into nested routes in SSR bold links
When I select the link Instrumented
And I select the link Target 421
And I reload the page
Then I see the following links being bolded
| Instrumented |
| Item Listing |
| Target 4## |
| Target 42# |
| Target 421 |
| field1 |
Scenario: Going deep down navigate around nested links bold correctly
When I select the link Instrumented
And I select the link Target 421
And I select the link Inspect path2/field3
Then I see the following links being bolded
| Instrumented |
| Item Listing |
| Target 4## |
| Target 42# |
| field3 |
And I see the following links not being bolded
| Target 421 |
| field1 |
Scenario: Going deep down navigate around nested links bold correctly, SSR
When I select the link Instrumented
And I select the link Target 421
And I select the link Inspect path2/field3
And I reload the page
Then I see the following links being bolded
| Instrumented |
| Item Listing |
| Target 4## |
| Target 42# |
| field3 |
And I see the following links not being bolded
| Target 421 |
| field1 |
Scenario: Going deep down back out nested routes reset bolded states
When I select the link Instrumented
And I select the link Target 421
And I select the link Counters
Then I see the following links being bolded
| Instrumented |
| Counters |
And I see the following links not being bolded
| Item Listing |
| Target 4## |
| Target 42# |
| Target 421 |
Scenario: Going deep down back out nested routes reset bolded states, SSR
When I select the link Instrumented
And I select the link Target 421
And I select the link Counters
And I reload the page
Then I see the following links being bolded
| Instrumented |
| Counters |
And I see the following links not being bolded
| Item Listing |
| Target 4## |
| Target 42# |
| Target 421 |

View File

@@ -81,3 +81,20 @@ pub async fn instrumented_counts(
Ok(())
}
pub async fn link_text_is_aria_current(client: &Client, text: &str) -> Result<()> {
let link = find::link_with_text(client, text).await?;
link.attr("aria-current").await?
.expect(format!("aria-current missing for {text}").as_str());
Ok(())
}
pub async fn link_text_is_not_aria_current(client: &Client, text: &str) -> Result<()> {
let link = find::link_with_text(client, text).await?;
link.attr("aria-current").await?
.map(|_| anyhow::bail!("aria-current mistakenly set for {text}"))
.unwrap_or(Ok(()))
}

View File

@@ -124,3 +124,12 @@ async fn component_message(client: &Client, id: &str) -> Result<String> {
Ok(text)
}
pub async fn link_with_text(client: &Client, text: &str) -> Result<Element> {
let link = client
.wait()
.for_element(Locator::LinkText(text))
.await
.expect(format!("Link not found by `{}`", text).as_str());
Ok(link)
}

View File

@@ -80,6 +80,58 @@ async fn i_see_the_second_count_is(
Ok(())
}
#[then(regex = r"^I see the (.*) link being bolded$")]
async fn i_see_the_link_being_bolded(
world: &mut AppWorld,
text: String,
) -> Result<()> {
let client = &world.client;
check::link_text_is_aria_current(client, &text).await?;
Ok(())
}
#[then(expr = "I see the following links being bolded")]
async fn i_see_the_following_links_being_bolded(
world: &mut AppWorld,
step: &Step,
) -> Result<()> {
let client = &world.client;
if let Some(table) = step.table.as_ref() {
for row in table.rows.iter() {
check::link_text_is_aria_current(client, &row[0]).await?;
}
}
Ok(())
}
#[then(regex = r"^I see the (.*) link not being bolded$")]
async fn i_see_the_link_being_not_bolded(
world: &mut AppWorld,
text: String,
) -> Result<()> {
let client = &world.client;
check::link_text_is_not_aria_current(client, &text).await?;
Ok(())
}
#[then(expr = "I see the following links not being bolded")]
async fn i_see_the_following_links_not_being_bolded(
world: &mut AppWorld,
step: &Step,
) -> Result<()> {
let client = &world.client;
if let Some(table) = step.table.as_ref() {
for row in table.rows.iter() {
check::link_text_is_not_aria_current(client, &row[0]).await?;
}
}
Ok(())
}
#[then(expr = "I see the following counters under section")]
#[then(expr = "the following counters under section")]
async fn i_see_the_following_counters_under_section(

View File

@@ -4,25 +4,6 @@ use leptos::prelude::*;
use serde::{Deserialize, Serialize};
use server_fn::ServerFnError;
pub fn shell(leptos_options: &LeptosOptions) -> impl IntoView {
view! {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<AutoReload options=leptos_options.clone() />
<HydrationScripts options=leptos_options.clone()/>
<link rel="stylesheet" id="leptos" href="/pkg/todo_app_sqlite_csr.css"/>
<link rel="shortcut icon" type="image/ico" href="/favicon.ico"/>
</head>
<body>
<TodoApp/>
</body>
</html>
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
pub struct Todo {

View File

@@ -1,6 +1,6 @@
[package]
name = "hydration_context"
version = "0.2.0-rc1"
version = "0.2.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -1,3 +1,8 @@
// #[wasm_bindgen(thread_local)] is deprecated in wasm-bindgen 0.2.96
// but the replacement is also only shipped in that version
// as a result, we'll just allow deprecated for now
#![allow(deprecated)]
use super::{SerializedDataId, SharedContext};
use crate::{PinnedFuture, PinnedStream};
use core::fmt::Debug;

View File

@@ -83,15 +83,14 @@ pub trait SharedContext: Debug {
/// Reads the current value of some data from the shared context, if it has been
/// sent from the server. This returns the serialized data as a `String` that should
/// be deserialized using [`Serializable::de`].
/// be deserialized.
///
/// On the server and in client-side rendered implementations, this should
/// always return [`None`].
fn read_data(&self, id: &SerializedDataId) -> Option<String>;
/// Returns a [`Future`] that resolves with a `String` that should
/// be deserialized using [`Serializable::de`] once the given piece of server
/// data has resolved.
/// be deserialized once the given piece of server data has resolved.
///
/// On the server and in client-side rendered implementations, this should
/// return a [`Future`] that is immediately ready with [`None`].
@@ -148,8 +147,8 @@ pub trait SharedContext: Debug {
/// Adds a `Future` to the set of “blocking resources” that should prevent the servers
/// response stream from beginning until all are resolved. The `Future` returned by
/// [`blocking_resources`](Self::blocking_resources) will not resolve until every `Future`
/// added by this method has resolved.
/// blocking resources will not resolve until every `Future` added by this method
/// has resolved.
///
/// In browser implementations, this should be a no-op.
fn defer_stream(&self, wait_for: PinnedFuture<()>);

View File

@@ -82,7 +82,7 @@ impl ResponseParts {
}
}
/// A wrapper for an Actix [`HttpRequest`](actix_web::HttpRequest) that allows it to be used in an
/// A wrapper for an Actix [`HttpRequest`] that allows it to be used in an
/// `Send`/`Sync` setting like Leptos's Context API.
#[derive(Debug, Clone)]
pub struct Request(SendWrapper<HttpRequest>);
@@ -419,12 +419,6 @@ pub fn handle_server_fns_with_context(
/// will include fallback content for any `<Suspense/>` nodes, and be immediately interactive,
/// but requires some client-side JavaScript.
///
/// The provides a [MetaContext] and a [RouterIntegrationContext] to apps context before
/// rendering it, and includes any meta tags injected using [leptos_meta].
///
/// The HTML stream is rendered using [render_to_stream](leptos::ssr::render_to_stream), and
/// includes everything described in the documentation for that function.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// use actix_web::{App, HttpServer};
@@ -465,7 +459,6 @@ pub fn handle_server_fns_with_context(
/// - [ResponseOptions]
/// - [Request]
/// - [MetaContext](leptos_meta::MetaContext)
/// - [RouterIntegrationContext](leptos_router::RouterIntegrationContext)
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -485,13 +478,6 @@ where
/// This stream will pause at each `<Suspense/>` node and wait for it to resolve before
/// sending down its HTML. The app will become interactive once it has fully loaded.
///
/// The provides a [MetaContext] and a [RouterIntegrationContext] to apps context before
/// rendering it, and includes any meta tags injected using [leptos_meta].
///
/// The HTML stream is rendered using
/// [render_to_stream_in_order](leptos::ssr::render_to_stream_in_order),
/// and includes everything described in the documentation for that function.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// use actix_web::{App, HttpServer};
@@ -534,7 +520,6 @@ where
/// This function always provides context values including the following types:
/// - [ResponseOptions]
/// - [Request]
/// - [RouterIntegrationContext](leptos_router::RouterIntegrationContext)
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -551,13 +536,7 @@ where
/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries
/// to route it using [leptos_router], asynchronously rendering an HTML page after all
/// `async` [Resource](leptos::Resource)s have loaded.
///
/// The provides a [MetaContext] and a [RouterIntegrationContext] to the apps context before
/// rendering it, and includes any meta tags injected using [leptos_meta].
///
/// The HTML stream is rendered using [render_to_string_async](leptos::ssr::render_to_string_async), and
/// includes everything described in the documentation for that function.
/// `async` resources have loaded.
///
/// This can then be set up at an appropriate route in your application:
/// ```
@@ -690,7 +669,6 @@ where
/// - [ResponseOptions]
/// - [Request]
/// - [MetaContext](leptos_meta::MetaContext)
/// - [RouterIntegrationContext](leptos_router::RouterIntegrationContext)
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -713,7 +691,7 @@ where
/// Returns an Actix [struct@Route](actix_web::Route) that listens for a `GET` request and tries
/// to route it using [leptos_router], asynchronously serving the page once all `async`
/// [Resource](leptos::Resource)s have loaded.
/// resources have loaded.
///
/// This function allows you to provide additional information to Leptos for your route.
/// It could be used to pass in Path Info, Connection Info, or anything your heart desires.

View File

@@ -11,7 +11,7 @@ edition.workspace = true
[dependencies]
any_spawner = { workspace = true, features = ["tokio"] }
hydration_context = { workspace = true }
axum = { version = "0.7.7", default-features = false, features = [
axum = { version = "0.7.9", default-features = false, features = [
"matched-path",
] }
dashmap = "6"
@@ -26,11 +26,11 @@ once_cell = "1"
parking_lot = "0.12.3"
tokio = { version = "1.41", default-features = false }
tower = { version = "0.5.1", features = ["util"] }
tower-http = "0.6.1"
tower-http = "0.6.2"
tracing = { version = "0.1.40", optional = true }
[dev-dependencies]
axum = "0.7.7"
axum = "0.7.9"
tokio = { version = "1.41", features = ["net", "rt-multi-thread"] }
[features]

View File

@@ -16,7 +16,7 @@
//! - `default`: supports running in a typical native Tokio/Axum environment
//! - `wasm`: with `default-features = false`, supports running in a JS Fetch-based
//! environment
//! - `experimental-islands`: activates Leptos [islands mode](https://leptos-rs.github.io/leptos/islands.html)
//! - `islands`: activates Leptos [islands mode](https://leptos-rs.github.io/leptos/islands.html)
//!
//! ### Important Note
//! Prior to 0.5, using `default-features = false` on `leptos_axum` simply did nothing. Now, it actively
@@ -197,9 +197,9 @@ impl ExtendResponse for AxumResponse {
/// 2. A server function that is called from WASM running in the client (e.g., a dispatched action
/// or a spawned `Future`).
/// 3. A `<form>` submitted to the server function endpoint using default browser APIs (often due
/// to using [`ActionForm`](leptos::form::ActionForm) without JS/WASM present.)
/// to using [`ActionForm`] without JS/WASM present.)
///
/// Using it with a non-blocking [`Resource`](leptos::server::Resource) will not work if you are using streaming rendering,
/// Using it with a non-blocking [`Resource`] will not work if you are using streaming rendering,
/// as the response's headers will already have been sent by the time the server function calls `redirect()`.
///
/// ### Implementation
@@ -442,12 +442,6 @@ pub type PinnedHtmlStream =
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
/// to route it using [leptos_router], serving an HTML stream of your application.
///
/// The provides a [MetaContext] and a [RouterIntegrationContext] to apps context before
/// rendering it, and includes any meta tags injected using [leptos_meta].
///
/// The HTML stream is rendered using [render_to_stream](leptos::ssr::render_to_stream), and
/// includes everything described in the documentation for that function.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// use axum::{handler::Handler, Router};
@@ -485,8 +479,7 @@ pub type PinnedHtmlStream =
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -537,12 +530,6 @@ where
/// This stream will pause at each `<Suspense/>` node and wait for it to resolve before
/// sending down its HTML. The app will become interactive once it has fully loaded.
///
/// The provides a [MetaContext] and a [RouterIntegrationContext] to apps context before
/// rendering it, and includes any meta tags injected using [leptos_meta].
///
/// The HTML stream is rendered using [render_to_stream_in_order], and includes everything described in
/// the documentation for that function.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// use axum::{handler::Handler, Router};
@@ -580,8 +567,7 @@ where
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -634,8 +620,7 @@ where
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -766,8 +751,7 @@ where
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -834,8 +818,7 @@ where
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -952,13 +935,7 @@ fn provide_contexts(
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
/// to route it using [leptos_router], asynchronously rendering an HTML page after all
/// `async` [Resource](leptos::Resource)s have loaded.
///
/// The provides a [MetaContext] and a [RouterIntegrationContext] to apps context before
/// rendering it, and includes any meta tags injected using [leptos_meta].
///
/// The HTML stream is rendered using [render_to_string_async], and includes everything described in
/// the documentation for that function.
/// `async` resources have loaded.
///
/// This can then be set up at an appropriate route in your application:
/// ```
@@ -998,8 +975,7 @@ fn provide_contexts(
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -1020,7 +996,7 @@ where
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
/// to route it using [leptos_router], asynchronously rendering an HTML page after all
/// `async` [Resource](leptos::Resource)s have loaded.
/// `async` resources have loaded.
///
/// This version allows us to pass Axum State/Extension/Extractor or other infro from Axum or network
/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides
@@ -1053,8 +1029,7 @@ where
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)
@@ -1088,7 +1063,7 @@ where
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
/// to route it using [leptos_router], asynchronously rendering an HTML page after all
/// `async` [Resource](leptos::Resource)s have loaded.
/// `async` resources have loaded.
///
/// This version allows us to pass Axum State/Extension/Extractor or other infro from Axum or network
/// layers above Leptos itself. To use it, you'll need to write your own handler function that provides
@@ -1121,8 +1096,7 @@ where
/// This function always provides context values including the following types:
/// - [`Parts`]
/// - [`ResponseOptions`]
/// - [`ServerMetaContext`](leptos_meta::ServerMetaContext)
/// - [`RouterIntegrationContext`](leptos_router::RouterIntegrationContext)
/// - [`ServerMetaContext`]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", fields(error), skip_all)

View File

@@ -86,7 +86,7 @@ tracing = [
]
nonce = ["base64", "rand"]
spin = ["leptos-spin-macro"]
experimental-islands = ["leptos_macro/experimental-islands", "dep:serde_json"]
islands = ["leptos_macro/islands", "dep:serde_json"]
trace-component-props = [
"leptos_macro/trace-component-props",
"leptos_dom/trace-component-props"
@@ -104,7 +104,7 @@ denylist = [
"rkyv", # was causing clippy issues on nightly
"trace-component-props",
"spin",
"experimental-islands",
"islands",
]
skip_feature_sets = [
["csr", "ssr"],

View File

@@ -31,13 +31,13 @@
//! *Notes*:
//! - The `render_number` prop can receive any type that implements `Fn(i32) -> String`.
//! - Callbacks are most useful when you want optional generic props.
//! - All callbacks implement the [`Callable`] trait, and can be invoked with `my_callback.run(input)`.
//! - All callbacks implement the [`Callable`](leptos::callback::Callable) trait, and can be invoked with `my_callback.run(input)`.
//! - The callback types implement [`Copy`], so they can easily be moved into and out of other closures, just like signals.
//!
//! # Types
//! This modules implements 2 callback types:
//! - [`Callback`]
//! - [`UnsyncCallback`]
//! - [`Callback`](leptos::callback::Callback)
//! - [`UnsyncCallback`](leptos::callback::UnsyncCallback)
//!
//! Use `SyncCallback` if the function is not `Sync` and `Send`.
@@ -223,14 +223,14 @@ mod tests {
#[test]
fn clone_callback() {
let callback = Callback::new(move |_no_clone: NoClone| NoClone {});
let _cloned = callback.clone();
let _cloned = callback;
}
#[test]
fn clone_unsync_callback() {
let callback =
UnsyncCallback::new(move |_no_clone: NoClone| NoClone {});
let _cloned = callback.clone();
let _cloned = callback;
}
#[test]

View File

@@ -246,7 +246,7 @@ where
}
}
/// A typed equivalent to [`ChildrenMut`], which takes a generic but preserves type information to
/// A typed equivalent to [`ChildrenFnMut`], which takes a generic but preserves type information to
/// allow the compiler to optimize the view more effectively.
pub struct TypedChildrenMut<T>(Box<dyn FnMut() -> View<T> + Send>);

View File

@@ -72,9 +72,7 @@ use web_sys::{
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
#[component]
pub fn ActionForm<ServFn>(
/// The action from which to build the form. This should include a URL, which can be generated
/// by default using [`create_server_action`](leptos_server::create_server_action) or added
/// manually using [`using_server_fn`](leptos_server::Action::using_server_fn).
/// The action from which to build the form.
action: ServerAction<ServFn>,
/// A [`NodeRef`] in which the `<form>` element should be stored.
#[prop(optional)]
@@ -149,9 +147,7 @@ where
/// progressively enhanced to use client-side routing.
#[component]
pub fn MultiActionForm<ServFn>(
/// The action from which to build the form. This should include a URL, which can be generated
/// by default using [create_server_action](leptos_server::create_server_action) or added
/// manually using [leptos_server::Action::using_server_fn].
/// The action from which to build the form.
action: ServerMultiAction<ServFn>,
/// A [`NodeRef`] in which the `<form>` element should be stored.
#[prop(optional)]

View File

@@ -47,7 +47,7 @@ pub fn AutoReload(
pub fn HydrationScripts(
/// Configuration options for this project.
options: LeptosOptions,
/// Should be `true` to hydrate in `experimental-islands` mode.
/// Should be `true` to hydrate in `islands` mode.
#[prop(optional)]
islands: bool,
/// A base url, not including a trailing slash

View File

@@ -39,7 +39,7 @@
//! server actions, forms, and server-sent events (SSE).
//! - **[`todomvc`]** shows the basics of building an isomorphic web app. Both the server and the client import the same app code.
//! The server renders the app directly to an HTML string, and the client hydrates that HTML to make it interactive.
//! You might also want to see how we use [`Effect::new`](leptos::prelude::Effect::new) to
//! You might also want to see how we use [`Effect::new`](leptos::prelude::Effect) to
//! [serialize JSON to `localStorage`](https://github.com/leptos-rs/leptos/blob/20af4928b2fffe017408d3f4e7330db22cf68277/examples/todomvc/src/lib.rs#L191-L209)
//! and [reactively call DOM methods](https://github.com/leptos-rs/leptos/blob/16f084a71268ac325fbc4a5e50c260df185eadb6/examples/todomvc/src/lib.rs#L292-L296)
//! on [references to elements](https://github.com/leptos-rs/leptos/blob/20af4928b2fffe017408d3f4e7330db22cf68277/examples/todomvc/src/lib.rs#L228).
@@ -78,7 +78,7 @@
//! + `async` interop: [`Resource`](leptos::prelude::Resource) for loading data using `async` functions
//! and [`Action`](leptos::prelude::Action) to mutate data or imperatively call `async` functions.
//! + reactions: [`Effect`](leptos::prelude::Effect) and [`RenderEffect`](leptos::prelude::RenderEffect).
//! - **Templating/Views**: the [`view`] macro and [`IntoView`](leptos::IntoView) trait.
//! - **Templating/Views**: the [`view`] macro and [`IntoView`] trait.
//! - **Routing**: the [`leptos_router`](https://docs.rs/leptos_router/latest/leptos_router/) crate
//! - **Server Functions**: the [`server`](macro@leptos::prelude::server) macro and [`ServerAction`](leptos::prelude::ServerAction).
//!
@@ -290,7 +290,7 @@ pub mod logging {
/// Utilities for working with asynchronous tasks.
pub mod task {
pub use any_spawner::Executor;
pub use any_spawner::{self, CustomExecutor, Executor};
use std::future::Future;
/// Spawns a thread-safe [`Future`].
@@ -318,10 +318,10 @@ pub mod task {
}
// these reexports are used in islands
#[cfg(feature = "experimental-islands")]
#[cfg(feature = "islands")]
#[doc(hidden)]
pub use serde;
#[cfg(feature = "experimental-islands")]
#[cfg(feature = "islands")]
#[doc(hidden)]
pub use serde_json;
#[cfg(feature = "tracing")]

File diff suppressed because it is too large Load Diff

View File

@@ -30,14 +30,14 @@ fn ws_from_str_test() {
#[test]
fn env_w_default_test() {
_ = temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("custom")
);
});
_ = temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("default")
@@ -47,14 +47,14 @@ fn env_w_default_test() {
#[test]
fn env_wo_default_test() {
_ = temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
assert_eq!(
env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(),
Some(String::from("custom"))
);
});
_ = temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
assert_eq!(env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(), None);
});
}

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_macro"
version = "0.7.0-rc1"
version = "0.7.0"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
@@ -13,7 +13,7 @@ edition.workspace = true
proc-macro = true
[dependencies]
attribute-derive = { version = "0.10.2", features = ["syn-full"] }
attribute-derive = { version = "0.10.3", features = ["syn-full"] }
cfg-if = "1.0"
html-escape = "0.2.13"
itertools = "0.13.0"
@@ -44,7 +44,7 @@ hydrate = []
ssr = ["server_fn_macro/ssr", "leptos/ssr"]
nightly = ["server_fn_macro/nightly"]
tracing = ["dep:tracing"]
experimental-islands = []
islands = []
trace-component-props = []
actix = ["server_fn_macro/actix"]
axum = ["server_fn_macro/axum"]

View File

@@ -1,4 +1,4 @@
//! Macros for use with the [`leptos`] framework.
//! Macros for use with the Leptos framework.
#![cfg_attr(feature = "nightly", feature(proc_macro_span))]
#![forbid(unsafe_code)]
@@ -272,8 +272,8 @@ pub fn view(tokens: TokenStream) -> TokenStream {
view_macro_impl(tokens, false)
}
/// The `template` macro behaves like [`view`], except that it wraps the entire tree in a
/// [`ViewTemplate`](leptos::prelude::ViewTemplate). This optimizes creation speed by rendering
/// The `template` macro behaves like [`view`](view!), except that it wraps the entire tree in a
/// [`ViewTemplate`](https://docs.rs/leptos/0.7.0-gamma3/leptos/prelude/struct.ViewTemplate.html). This optimizes creation speed by rendering
/// most of the view into a `<template>` tag with HTML rendered at compile time, then hydrating it.
/// In exchange, there is a small binary size overhead.
#[proc_macro_error2::proc_macro_error]
@@ -366,7 +366,7 @@ fn normalized_call_site(site: proc_macro::Span) -> Option<String> {
}
}
/// This behaves like the [`view`] macro, but loads the view from an external file instead of
/// This behaves like the [`view`](view!) macro, but loads the view from an external file instead of
/// parsing it inline.
///
/// This is designed to allow editing views in a separate file, if this improves a user's workflow.
@@ -559,10 +559,10 @@ pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
}
/// Defines a component as an interactive island when you are using the
/// `experimental-islands` feature of Leptos. Apart from the macro name,
/// `islands` feature of Leptos. Apart from the macro name,
/// the API is the same as the [`component`](macro@component) macro.
///
/// When you activate the `experimental-islands` feature, every `#[component]`
/// When you activate the `islands` feature, every `#[component]`
/// is server-only by default. This "default to server" behavior is important:
/// you opt into shipping code to the client, rather than opting out. You can
/// opt into client-side interactivity for any given component by changing from
@@ -639,7 +639,7 @@ pub fn island(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
abort!(
transparent,
"only `transparent` is supported";
help = "try `#[component(transparent)]` or `#[component]`"
help = "try `#[island(transparent)]` or `#[island]`"
);
}

View File

@@ -197,7 +197,7 @@ enum InertElementBuilder<'a> {
},
}
impl<'a> ToTokens for InertElementBuilder<'a> {
impl ToTokens for InertElementBuilder<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
InertElementBuilder::GlobalClass { strs, .. } => {
@@ -219,7 +219,7 @@ enum GlobalClassItem<'a> {
String(String),
}
impl<'a> ToTokens for GlobalClassItem<'a> {
impl ToTokens for GlobalClassItem<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let addl_tokens = match self {
GlobalClassItem::Global(v) => v.to_token_stream(),
@@ -652,6 +652,18 @@ pub(crate) fn element_to_tokens(
},
_ => None,
};
if let NodeAttribute::Attribute(a) = a {
if let Some(Tuple(_)) = a.value() {
return Ordering::Greater;
}
}
if let NodeAttribute::Attribute(b) = b {
if let Some(Tuple(_)) = b.value() {
return Ordering::Less;
}
}
match (key_a.as_deref(), key_b.as_deref()) {
(Some("class"), Some("class")) | (Some("style"), Some("style")) => {
Ordering::Equal
@@ -755,7 +767,7 @@ pub(crate) fn element_to_tokens(
let name = node.name().to_string();
// link custom ident to name span for IDE docs
let custom = Ident::new("custom", name.span());
quote! { ::leptos::tachys::html::element::#custom(#name) }
quote_spanned! { node.name().span() => ::leptos::tachys::html::element::#custom(#name) }
} else if is_svg_element(&tag) {
parent_type = TagType::Svg;
let name = if tag == "use" || tag == "use_" {
@@ -763,33 +775,33 @@ pub(crate) fn element_to_tokens(
} else {
name.to_token_stream()
};
quote! { ::leptos::tachys::svg::#name() }
quote_spanned! { node.name().span() => ::leptos::tachys::svg::#name() }
} else if is_math_ml_element(&tag) {
parent_type = TagType::Math;
quote! { ::leptos::tachys::mathml::#name() }
quote_spanned! { node.name().span() => ::leptos::tachys::mathml::#name() }
} else if is_ambiguous_element(&tag) {
match parent_type {
TagType::Unknown => {
// We decided this warning was too aggressive, but I'll leave it here in case we want it later
/* proc_macro_error2::emit_warning!(name.span(), "The view macro is assuming this is an HTML element, \
but it is ambiguous; if it is an SVG or MathML element, prefix with svg:: or math::"); */
quote! {
quote_spanned! { node.name().span() =>
::leptos::tachys::html::element::#name()
}
}
TagType::Html => {
quote! { ::leptos::tachys::html::element::#name() }
quote_spanned! { node.name().span() => ::leptos::tachys::html::element::#name() }
}
TagType::Svg => {
quote! { ::leptos::tachys::svg::#name() }
quote_spanned! { node.name().span() => ::leptos::tachys::svg::#name() }
}
TagType::Math => {
quote! { ::leptos::tachys::math::#name() }
quote_spanned! { node.name().span() => ::leptos::tachys::math::#name() }
}
}
} else {
parent_type = TagType::Html;
quote! { ::leptos::tachys::html::element::#name() }
quote_spanned! { name.span() => ::leptos::tachys::html::element::#name() }
};
/* TODO restore this
@@ -1710,7 +1722,7 @@ fn tuple_name(name: &str, node: &KeyedAttribute) -> TupleName {
TupleName::None
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
enum TupleName {
None,
Str(String),

View File

@@ -1,4 +1,4 @@
//! Utilities for communicating between the server and the client with [`leptos`].
//! Utilities for communicating between the server and the client with Leptos.
#![deny(missing_docs)]
#![forbid(unsafe_code)]

View File

@@ -253,7 +253,8 @@ where
}
}
/// A [`Future`] that is ready when an [`ArcAsyncDerived`] is finished loading or reloading,
/// A [`Future`] that is ready when an
/// [`ArcAsyncDerived`](reactive_graph::computed::ArcAsyncDerived) is finished loading or reloading,
/// and contains its value. `.await`ing this clones the value `T`.
pub struct OnceResourceFuture<T> {
source: AnySource,

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_meta"
version = "0.7.0-rc1"
version = "0.7.0"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"

View File

@@ -7,8 +7,7 @@
//! using the [`Leptos`](https://github.com/leptos-rs/leptos) web framework.
//!
//! Document metadata is updated automatically when running in the browser. For server-side
//! rendering, after the component tree is rendered to HTML, [`MetaContext::dehydrate`] can generate
//! HTML that should be injected into the `<head>` of the HTML document being rendered.
//! rendering, after the component tree is rendered to HTML, [`ServerMetaContextOutput::inject_meta_context`] will inject meta tags into a stream of HTML inside the `<head>`.
//!
//! ```
//! use leptos::prelude::*;

View File

@@ -1,6 +1,6 @@
[package]
name = "next_tuple"
version = "0.1.0-rc1"
version = "0.1.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -70,7 +70,7 @@ pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
Owned(<T as ToOwned>::Owned),
}
impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
impl<T: ?Sized + ToOwned> Oco<'_, T> {
/// Converts the value into an owned value.
pub fn into_owned(self) -> <T as ToOwned>::Owned {
match self {
@@ -339,7 +339,7 @@ where
}
}
impl<'a, 'b, A: ?Sized, B: ?Sized> PartialEq<Oco<'b, B>> for Oco<'a, A>
impl<'b, A: ?Sized, B: ?Sized> PartialEq<Oco<'b, B>> for Oco<'_, A>
where
A: PartialEq<B>,
A: ToOwned,
@@ -352,7 +352,7 @@ where
impl<T: ?Sized + ToOwned + Eq> Eq for Oco<'_, T> {}
impl<'a, 'b, A: ?Sized, B: ?Sized> PartialOrd<Oco<'b, B>> for Oco<'a, A>
impl<'b, A: ?Sized, B: ?Sized> PartialOrd<Oco<'b, B>> for Oco<'_, A>
where
A: PartialOrd<B>,
A: ToOwned,
@@ -551,7 +551,7 @@ impl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, &'b [T
impl_slice_eq!([T: PartialEq] (where [T]: ToOwned), Oco<'_, [T]>, Vec<T>);
impl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, Cow<'b, [T]>);
impl<'a, 'b> Add<&'b str> for Oco<'a, str> {
impl<'b> Add<&'b str> for Oco<'_, str> {
type Output = Oco<'static, str>;
fn add(self, rhs: &'b str) -> Self::Output {
@@ -559,7 +559,7 @@ impl<'a, 'b> Add<&'b str> for Oco<'a, str> {
}
}
impl<'a, 'b> Add<Cow<'b, str>> for Oco<'a, str> {
impl<'b> Add<Cow<'b, str>> for Oco<'_, str> {
type Output = Oco<'static, str>;
fn add(self, rhs: Cow<'b, str>) -> Self::Output {
@@ -567,7 +567,7 @@ impl<'a, 'b> Add<Cow<'b, str>> for Oco<'a, str> {
}
}
impl<'a, 'b> Add<Oco<'b, str>> for Oco<'a, str> {
impl<'b> Add<Oco<'b, str>> for Oco<'_, str> {
type Output = Oco<'static, str>;
fn add(self, rhs: Oco<'b, str>) -> Self::Output {

View File

@@ -21,6 +21,6 @@ CREATE TABLE IF NOT EXISTS google_tokens (
access_secret TEXT NOT NULL,
refresh_secret TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) CONFLICT REPLACE
FOREIGN KEY (user_id) REFERENCES users(id)
);

View File

@@ -64,7 +64,7 @@ pub async fn refresh_token(email: String) -> Result<u64, ServerFnError> {
.execute(&pool)
.await?;
sqlx::query(
"INSERT INTO google_tokens (user_id,access_secret,refresh_secret) \
"INSERT OR REPLACE INTO google_tokens (user_id,access_secret,refresh_secret) \
VALUES (?,?,?)",
)
.bind(user.id)

View File

@@ -96,7 +96,7 @@ async fn main() {
let client = oauth2::basic::BasicClient::new(
oauth2::ClientId::new(
std::env::var("G_AUTH_CLIENT_ID")
.expect("G_AUTH_CLIENT Env var to be set."),
.expect("G_AUTH_CLIENT_ID Env var to be set."),
),
Some(oauth2::ClientSecret::new(
std::env::var("G_AUTH_SECRET")

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_graph"
version = "0.1.0-rc1"
version = "0.1.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -19,7 +19,7 @@ pub(crate) use inner::MemoInner;
pub use memo::*;
pub use selector::*;
/// Derives a reactive slice of an [`RwSignal`](crate::signal::RwSignal).
/// Derives a reactive slice of an [`RwSignal`].
///
/// Slices have the same guarantees as [`Memo`s](crate::computed::Memo):
/// they only emit their value when it has actually been changed.

View File

@@ -117,6 +117,7 @@ pub struct ArcAsyncDerived<T> {
pub(crate) loading: Arc<AtomicBool>,
}
#[allow(dead_code)]
pub(crate) trait BlockingLock<T> {
fn blocking_read_arc(self: &Arc<Self>)
-> async_lock::RwLockReadGuardArc<T>;
@@ -583,19 +584,17 @@ impl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {
fn try_read_untracked(&self) -> Option<Self::Value> {
if let Some(suspense_context) = use_context::<SuspenseContext>() {
if self.value.blocking_read().is_none() {
let handle = suspense_context.task_id();
let ready = SpecialNonReactiveFuture::new(self.ready());
crate::spawn(async move {
ready.await;
drop(handle);
});
self.inner
.write()
.or_poisoned()
.suspenses
.push(suspense_context);
}
let handle = suspense_context.task_id();
let ready = SpecialNonReactiveFuture::new(self.ready());
crate::spawn(async move {
ready.await;
drop(handle);
});
self.inner
.write()
.or_poisoned()
.suspenses
.push(suspense_context);
}
AsyncPlain::try_new(&self.value).map(ReadGuard::new)
}

View File

@@ -124,7 +124,7 @@ pub fn log_warning(text: Arguments) {
}
}
/// Calls [`Executor::spawn`], but ensures that the task also runs in the current arena, if
/// Calls [`Executor::spawn`](any_spawner::Executor), but ensures that the task also runs in the current arena, if
/// multithreaded arena sandboxing is enabled.
pub fn spawn(task: impl Future<Output = ()> + Send + 'static) {
#[cfg(feature = "sandboxed-arenas")]
@@ -133,8 +133,9 @@ pub fn spawn(task: impl Future<Output = ()> + Send + 'static) {
any_spawner::Executor::spawn(task);
}
/// Calls [`Executor::spawn_local`], but ensures that the task runs under the current reactive [`Owner`]
/// and [`Observed`]. Does not cancel the task if the owner is cleaned up.
/// Calls [`Executor::spawn_local`](any_spawner::Executor), but ensures that the task runs under the current reactive [`Owner`](crate::owner::Owner) and observer.
///
/// Does not cancel the task if the owner is cleaned up.
pub fn spawn_local_scoped(task: impl Future<Output = ()> + 'static) {
let task = ScopedFuture::new(task);
@@ -144,8 +145,9 @@ pub fn spawn_local_scoped(task: impl Future<Output = ()> + 'static) {
any_spawner::Executor::spawn_local(task);
}
/// Calls [`Executor::spawn_local`], but ensures that the task runs under the current reactive [`Owner`]
/// and [`Observed`]. Cancels the task if the owner is cleaned up.
/// Calls [`Executor::spawn_local`](any_spawner::Executor), but ensures that the task runs under the current reactive [`Owner`](crate::owner::Owner) and observer.
///
/// Cancels the task if the owner is cleaned up.
pub fn spawn_local_scoped_with_cancellation(
task: impl Future<Output = ()> + 'static,
) {

View File

@@ -30,7 +30,7 @@ impl<T> StorageAccess<T> for SendWrapper<T> {
}
}
/// A way of storing a [`ArenaItem`], either as itself or with a wrapper to make it threadsafe.
/// A way of storing an [`ArenaItem`](super::arena_item::ArenaItem), either as itself or with a wrapper to make it threadsafe.
///
/// This exists because all items stored in the arena must be `Send + Sync`, but in single-threaded
/// environments you might want or need to use thread-unsafe types.

View File

@@ -220,7 +220,7 @@ where
}
}
/// Helper trait to implement flatten() on Option<&Option<T>>.
/// Helper trait to implement flatten() on `Option<&Option<T>>`.
pub trait FlattenOptionRefOption {
/// The type of the value contained in the double option.
type Value;

View File

@@ -16,7 +16,7 @@
//! | Trait | Mode | Description |
//! |-------------------|-------|---------------------------------------------------------------------------------------|
//! | [`Track`] | — | Tracks changes to this value, adding it as a source of the current reactive observer. |
//! | [`Trigger`] | — | Notifies subscribers that this value has changed. |
//! | [`Notify`] | — | Notifies subscribers that this value has changed. |
//! | [`ReadUntracked`] | Guard | Gives immutable access to the value of this signal. |
//! | [`Write`] | Guard | Gives mutable access to the value of this signal.
//!
@@ -34,7 +34,7 @@
//! | Trait | Mode | Composition | Description
//! |---------------------|---------------|-----------------------------------|------------
//! | [`UpdateUntracked`] | `fn(&mut T)` | [`Write`] | Applies closure to the current value to update it, but doesn't notify subscribers.
//! | [`Update`] | `fn(&mut T)` | [`UpdateUntracked`] + [`Trigger`] | Applies closure to the current value to update it, and notifies subscribers.
//! | [`Update`] | `fn(&mut T)` | [`UpdateUntracked`] + [`Notify`] | Applies closure to the current value to update it, and notifies subscribers.
//! | [`Set`] | `T` | [`Update`] | Sets the value to a new value, and notifies subscribers.
//!
//! ## Using the Traits

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_stores"
version = "0.1.0-rc1"
version = "0.1.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -7,6 +7,7 @@ use reactive_graph::{
owner::Storage,
traits::{
DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
Write,
},
};
use std::{
@@ -31,7 +32,8 @@ where
trigger: StoreFieldTrigger,
get_trigger: Arc<dyn Fn(StorePath) -> StoreFieldTrigger + Send + Sync>,
read: Arc<dyn Fn() -> Option<StoreFieldReader<T>> + Send + Sync>,
write: Arc<dyn Fn() -> Option<StoreFieldWriter<T>> + Send + Sync>,
pub(crate) write:
Arc<dyn Fn() -> Option<StoreFieldWriter<T>> + Send + Sync>,
keys: Arc<dyn Fn() -> Option<KeyMap> + Send + Sync>,
track_field: Arc<dyn Fn() + Send + Sync>,
}
@@ -329,6 +331,22 @@ impl<T> ReadUntracked for ArcField<T> {
}
}
impl<T> Write for ArcField<T> {
type Value = T;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
(self.write)()
}
fn try_write_untracked(
&self,
) -> Option<impl DerefMut<Target = Self::Value>> {
let mut guard = (self.write)()?;
guard.untrack();
Some(guard)
}
}
impl<T> IsDisposed for ArcField<T> {
fn is_disposed(&self) -> bool {
false

View File

@@ -6,10 +6,18 @@ use crate::{
};
use reactive_graph::{
owner::{ArenaItem, Storage, SyncStorage},
traits::{DefinedAt, IsDisposed, Notify, ReadUntracked, Track},
traits::{
DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
Write,
},
unwrap_signal,
};
use std::{fmt::Debug, hash::Hash, ops::IndexMut, panic::Location};
use std::{
fmt::Debug,
hash::Hash,
ops::{DerefMut, IndexMut},
panic::Location,
};
/// Wraps access to a single field of type `T`.
///
@@ -204,6 +212,24 @@ where
}
}
impl<T> Write for Field<T> {
type Value = T;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
self.inner.try_get_value().and_then(|inner| (inner.write)())
}
fn try_write_untracked(
&self,
) -> Option<impl DerefMut<Target = Self::Value>> {
self.inner.try_get_value().and_then(|inner| {
let mut guard = (inner.write)()?;
guard.untrack();
Some(guard)
})
}
}
impl<T, S> IsDisposed for Field<T, S> {
fn is_disposed(&self) -> bool {
self.inner.is_disposed()

View File

@@ -1,5 +1,5 @@
use crate::{StoreField, Subfield};
use reactive_graph::traits::Read;
use reactive_graph::traits::{Read, ReadUntracked};
use std::ops::Deref;
/// Extends optional store fields, with the ability to unwrap or map over them.
@@ -23,12 +23,23 @@ where
self,
map_fn: impl FnOnce(Subfield<Self, Option<Self::Output>, Self::Output>) -> U,
) -> Option<U>;
/// Unreactively maps over the field.
///
/// This returns `None` if the subfield is currently `None`,
/// and a new store subfield with the inner value if it is `Some`. This is an unreactive variant of
/// `[OptionStoreExt::map]`, and will not cause the reactive context to re-run if the field changes.
fn map_untracked<U>(
self,
map_fn: impl FnOnce(Subfield<Self, Option<Self::Output>, Self::Output>) -> U,
) -> Option<U>;
}
impl<T, S> OptionStoreExt for S
where
S: StoreField<Value = Option<T>> + Read,
S: StoreField<Value = Option<T>> + Read + ReadUntracked,
<S as Read>::Value: Deref<Target = Option<T>>,
<S as ReadUntracked>::Value: Deref<Target = Option<T>>,
{
type Output = T;
@@ -51,6 +62,17 @@ where
None
}
}
fn map_untracked<U>(
self,
map_fn: impl FnOnce(Subfield<S, Option<T>, T>) -> U,
) -> Option<U> {
if self.read_untracked().is_some() {
Some(map_fn(self.unwrap()))
} else {
None
}
}
}
#[cfg(test)]

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_stores_macro"
version = "0.1.0-rc1"
version = "0.1.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_router"
version = "0.7.0-rc1"
version = "0.7.0"
authors = ["Greg Johnston", "Ben Wishovich"]
license = "MIT"
readme = "../README.md"

View File

@@ -13,8 +13,8 @@ use crate::{
resolve_path::resolve_path,
ChooseView, MatchNestedRoutes, NestedRoute, RouteDefs, SsrMode,
};
use either_of::Either;
use leptos::prelude::*;
use either_of::EitherOf3;
use leptos::{children, prelude::*};
use reactive_graph::{
owner::{provide_context, use_context, Owner},
signal::ArcRwSignal,
@@ -64,8 +64,8 @@ pub fn Router<Chil>(
//#[prop(optional)]
//trailing_slash: TrailingSlash,
/// The `<Router/>` should usually wrap your whole page. It can contain
/// any elements, and should include a [`Routes`](crate::Routes) component somewhere
/// to define and display [`Route`](crate::Route)s.
/// any elements, and should include a [`Routes`] component somewhere
/// to define and display [`Route`]s.
children: TypedChildren<Chil>,
) -> impl IntoView
where
@@ -328,9 +328,10 @@ where
/// and the element it should display.
#[component(transparent)]
pub fn Route<Segments, View>(
/// The path fragment that this route should match. This can be created using the [`path`]
/// macro, or path segments ([`StaticSegment`], [`ParamSegment`], [`WildcardSegment`], and
/// [`OptionalParamSegment`]).
/// The path fragment that this route should match. This can be created using the
/// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),
/// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and
/// [`OptionalParamSegment`](crate::OptionalParamSegment)).
path: Segments,
/// The view for this route.
view: View,
@@ -349,9 +350,10 @@ where
/// and the element it should display.
#[component(transparent)]
pub fn ParentRoute<Segments, View, Children>(
/// The path fragment that this route should match. This can be created using the [`path`]
/// macro, or path segments ([`StaticSegment`], [`ParamSegment`], [`WildcardSegment`], and
/// [`OptionalParamSegment`]).
/// The path fragment that this route should match. This can be created using the
/// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),
/// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and
/// [`OptionalParamSegment`](crate::OptionalParamSegment)).
path: Segments,
/// The view for this route.
view: View,
@@ -374,9 +376,10 @@ where
/// redirects to `redirect_path` instead of displaying its `view`.
#[component(transparent)]
pub fn ProtectedRoute<Segments, ViewFn, View, C, PathFn, P>(
/// The path fragment that this route should match. This can be created using the [`path`]
/// macro, or path segments ([`StaticSegment`], [`ParamSegment`], [`WildcardSegment`], and
/// [`OptionalParamSegment`]).
/// The path fragment that this route should match. This can be created using the
/// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),
/// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and
/// [`OptionalParamSegment`](crate::OptionalParamSegment)).
path: Segments,
/// The view for this route.
view: ViewFn,
@@ -386,6 +389,9 @@ pub fn ProtectedRoute<Segments, ViewFn, View, C, PathFn, P>(
condition: C,
/// The path that will be redirected to if the condition is `Some(false)`.
redirect_path: PathFn,
/// Will be displayed while the condition is pending. By default this is the empty view.
#[prop(optional, into)]
fallback: children::ViewFn,
/// The mode that this route prefers during server-side rendering.
/// Defaults to out-of-order streaming.
#[prop(optional)]
@@ -398,23 +404,26 @@ where
PathFn: Fn() -> P + Send + Clone + 'static,
P: Display + 'static,
{
let fallback = move || fallback.run();
let view = move || {
let condition = condition.clone();
let redirect_path = redirect_path.clone();
let view = view.clone();
let fallback = fallback.clone();
(view! {
<Transition>
<Transition fallback=fallback.clone()>
{move || {
let condition = condition();
let view = view.clone();
let redirect_path = redirect_path.clone();
let fallback = fallback.clone();
Unsuspend::new(move || match condition {
Some(true) => Either::Left(view()),
Some(true) => EitherOf3::A(view()),
#[allow(clippy::unit_arg)]
Some(false) => {
Either::Right(view! { <Redirect path=redirect_path()/> }.into_inner())
EitherOf3::B(view! { <Redirect path=redirect_path()/> }.into_inner())
}
None => Either::Right(()),
None => EitherOf3::C(fallback()),
})
}}
@@ -427,9 +436,10 @@ where
#[component(transparent)]
pub fn ProtectedParentRoute<Segments, ViewFn, View, C, PathFn, P, Children>(
/// The path fragment that this route should match. This can be created using the [`path`]
/// macro, or path segments ([`StaticSegment`], [`ParamSegment`], [`WildcardSegment`], and
/// [`OptionalParamSegment`]).
/// The path fragment that this route should match. This can be created using the
/// [`path`](crate::path) macro, or path segments ([`StaticSegment`](crate::StaticSegment),
/// [`ParamSegment`](crate::ParamSegment), [`WildcardSegment`](crate::WildcardSegment), and
/// [`OptionalParamSegment`](crate::OptionalParamSegment)).
path: Segments,
/// The view for this route.
view: ViewFn,
@@ -437,6 +447,9 @@ pub fn ProtectedParentRoute<Segments, ViewFn, View, C, PathFn, P, Children>(
/// the page, `Some(false)` means the user cannot access the page, and `None` means this
/// information is still loading.
condition: C,
/// Will be displayed while the condition is pending. By default this is the empty view.
#[prop(optional, into)]
fallback: children::ViewFn,
/// The path that will be redirected to if the condition is `Some(false)`.
redirect_path: PathFn,
/// Nested child routes.
@@ -453,17 +466,21 @@ where
PathFn: Fn() -> P + Send + Clone + 'static,
P: Display + 'static,
{
let fallback = move || fallback.run();
let children = children.into_inner();
let view = move || {
let condition = condition.clone();
let redirect_path = redirect_path.clone();
let fallback = fallback.clone();
let view = view.clone();
let owner = Owner::current().unwrap();
let view = {
let fallback = fallback.clone();
move || {
let condition = condition();
let view = view.clone();
let redirect_path = redirect_path.clone();
let fallback = fallback.clone();
let owner = owner.clone();
Unsuspend::new(move || match condition {
// reset the owner so that things like providing context work
@@ -472,16 +489,16 @@ where
//
// clippy: not redundant, a FnOnce vs FnMut issue
#[allow(clippy::redundant_closure)]
Some(true) => Either::Left(owner.with(|| view())),
Some(true) => EitherOf3::A(owner.with(|| view())),
#[allow(clippy::unit_arg)]
Some(false) => Either::Right(
Some(false) => EitherOf3::B(
view! { <Redirect path=redirect_path()/> }.into_inner(),
),
None => Either::Right(()),
None => EitherOf3::C(fallback()),
})
}
};
(view! { <Transition>{view}</Transition> }).into_any()
(view! { <Transition fallback>{view}</Transition> }).into_any()
};
NestedRoute::new(path, view).ssr_mode(ssr).child(children)
}

View File

@@ -17,7 +17,8 @@
//! and are rendered by different components. This means you can navigate between siblings
//! in this tree without re-rendering or triggering any change in the parent routes.
//!
//! 3. **Progressive enhancement.** The [`A`] and [`Form`] components resolve any relative
//! 3. **Progressive enhancement.** The [`A`](crate::components::A) and
//! [`Form`](crate::components::Form) components resolve any relative
//! nested routes, render actual `<a>` and `<form>` elements, and (when possible)
//! upgrading them to handle those navigations with client-side routing. If youre using
//! them with server-side rendering (with or without hydration), they just work,

View File

@@ -9,8 +9,7 @@ pub use static_segment::*;
/// or URL segment.
///
/// This is a "horizontal" matching: i.e., it treats a tuple of route segments
/// as subsequent segments of the URL and tries to match them all. For a "vertical"
/// matching that sees a tuple as alternatives to one another, see [`RouteChild`](super::RouteChild).
/// as subsequent segments of the URL and tries to match them all.
pub trait PossibleRouteMatch {
const OPTIONAL: bool = false;

View File

@@ -11,7 +11,7 @@ mod vertical;
use crate::{static_routes::RegenerationFn, Method, SsrMode};
pub use horizontal::*;
pub use nested::*;
use std::{borrow::Cow, collections::HashSet};
use std::{borrow::Cow, collections::HashSet, sync::atomic::Ordering};
pub use vertical::*;
#[derive(Debug)]
@@ -64,10 +64,7 @@ where
} else {
(base.as_ref(), path)
};
match path.strip_prefix(base) {
Some(path) => path,
None => return None,
}
path.strip_prefix(base)?
}
};
@@ -94,6 +91,16 @@ where
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct RouteMatchId(pub(crate) u16);
impl RouteMatchId {
/// Creates a new match ID based on the current route ID used in nested route generation.
///
/// In general, you do not need this; it should only be used for custom route matching behavior
/// in a library that creates its own route types.
pub fn new_from_route_id() -> RouteMatchId {
RouteMatchId(ROUTE_ID.fetch_add(1, Ordering::Relaxed))
}
}
pub trait MatchInterface {
type Child: MatchInterface + MatchParams + 'static;

View File

@@ -13,7 +13,7 @@ use std::{
mod tuples;
static ROUTE_ID: AtomicU16 = AtomicU16::new(1);
pub(crate) static ROUTE_ID: AtomicU16 = AtomicU16::new(1);
#[derive(Debug, PartialEq, Eq)]
pub struct NestedRoute<Segments, Children, Data, View> {

View File

@@ -1,6 +1,6 @@
use crate::location::State;
/// Options that can be used to configure a navigation. Used with [use_navigate](crate::use_navigate).
/// Options that can be used to configure a navigation. Used with [use_navigate](crate::hooks::use_navigate).
#[derive(Clone, Debug)]
pub struct NavigateOptions {
/// Whether the URL being navigated to should be resolved relative to the current route.

View File

@@ -24,7 +24,7 @@ impl ParamsMap {
/// Inserts a value into the map.
///
/// If a value with that key already exists, the new value will be added to it.
/// To replace the value instead, see [`replace`].
/// To replace the value instead, see [`replace`](Self::replace).
pub fn insert(&mut self, key: impl Into<Cow<'static, str>>, value: String) {
let value = Url::unescape(&value);

View File

@@ -44,8 +44,8 @@ pub enum SsrMode {
/// of the page will not be interactive until the suspended chunks have loaded.
InOrder,
/// **`Async`**: Load all resources on the server. Wait until all data are loaded, and render HTML in one sweep.
/// - *Pros*: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.
/// - *Cons*: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.
/// - *Pros*: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.
/// - *Cons*: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.
Async,
/// **`Static`**: Renders the page when the server starts up, or incrementally, using the
/// configuration provided by a [`StaticRoute`].

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_router_macro"
version = "0.7.0-rc1"
version = "0.7.0"
authors = ["Greg Johnston", "Ben Wishovich"]
license = "MIT"
readme = "../README.md"

2555
server_fn/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ once_cell = "1.20"
actix-web = { version = "4.9", optional = true }
# axum
axum = { version = "0.7.7", optional = true, default-features = false, features = [
axum = { version = "0.7.9", optional = true, default-features = false, features = [
"multipart",
] }
tower = { version = "0.5.1", optional = true }

View File

@@ -1,6 +1,6 @@
[package]
name = "tachys"
version = "0.1.0-rc1"
version = "0.1.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -267,7 +267,7 @@ impl<T: IntoClass> IntoClass for Option<T> {
}
}
impl<'a> IntoClass for &'a str {
impl IntoClass for &str {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Self;

View File

@@ -9,8 +9,9 @@ where
E: AsRef<str>,
{
HtmlElement {
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
tag: Custom(tag),
attributes: (),
children: (),
}

View File

@@ -10,8 +10,8 @@ use crate::{
use wasm_bindgen::JsValue;
use web_sys::Element;
/// Extends the [`Element`](Renderer::Element) type of a [`Renderer`], allowing you to add
/// attributes and children to the element's built state at runtime, with a similar API to how they
/// Extends an HTML element, allowing you to add attributes and children to the
/// element's built state at runtime, with a similar API to how they
/// can be added to the static view tree at compile time.
///
/// ```rust,ignore

View File

@@ -25,10 +25,11 @@ macro_rules! html_element_inner {
{
HtmlElement {
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
tag: $struct_name,
attributes: (),
children: (),
}
}
@@ -55,10 +56,17 @@ macro_rules! html_element_inner {
At: NextTuple,
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
{
let HtmlElement { tag, children, attributes } = self;
HtmlElement {
let HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes
} = self;
HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
}
@@ -118,14 +126,16 @@ macro_rules! html_self_closing_elements {
paste::paste! {
$(
#[$meta]
#[track_caller]
pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
where
{
HtmlElement {
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
attributes: (),
children: (),
tag: [<$tag:camel>],
}
}
@@ -138,7 +148,6 @@ macro_rules! html_self_closing_elements {
impl<At> HtmlElement<[<$tag:camel>], At, ()>
where
At: Attribute,
{
$(
#[doc = concat!("The [`", stringify!($attr), "`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/", stringify!($tag), "#", stringify!($attr) ,") attribute on `<", stringify!($tag), ">`.")]
@@ -151,13 +160,18 @@ macro_rules! html_self_closing_elements {
V: AttributeValue,
At: NextTuple,
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
{
let HtmlElement { tag, children, attributes,
let HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes,
} = self;
HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
}

View File

@@ -308,7 +308,7 @@ impl InnerHtmlValue for Arc<str> {
}
}
impl<'a> InnerHtmlValue for &'a str {
impl InnerHtmlValue for &str {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Self;

View File

@@ -1,6 +1,8 @@
#[cfg(debug_assertions)]
use crate::hydration::set_currently_hydrating;
use crate::{
html::attribute::Attribute,
hydration::Cursor,
hydration::{failed_to_cast_element, Cursor},
renderer::{CastFrom, Rndr},
ssr::StreamBuilder,
view::{
@@ -24,10 +26,14 @@ pub use custom::*;
pub use element_ext::*;
pub use elements::*;
pub use inner_html::*;
#[cfg(debug_assertions)]
use std::panic::Location;
/// The typed representation of an HTML element.
#[derive(Debug, PartialEq, Eq)]
pub struct HtmlElement<E, At, Ch> {
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) tag: E,
pub(crate) attributes: At,
pub(crate) children: Ch,
@@ -36,8 +42,9 @@ pub struct HtmlElement<E, At, Ch> {
impl<E: Clone, At: Clone, Ch: Clone> Clone for HtmlElement<E, At, Ch> {
fn clone(&self) -> Self {
HtmlElement {
#[cfg(debug_assertions)]
defined_at: self.defined_at,
tag: self.tag.clone(),
attributes: self.attributes.clone(),
children: self.children.clone(),
}
@@ -75,14 +82,16 @@ where
fn child(self, child: NewChild) -> Self::Output {
let HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
attributes,
children,
} = self;
HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
attributes,
children: children.next_tuple(child.into_render()),
}
@@ -103,11 +112,15 @@ where
attr: NewAttr,
) -> Self::Output<NewAttr> {
let HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
attributes,
children,
} = self;
HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
attributes: attributes.add_any_attr(attr),
children,
@@ -229,8 +242,9 @@ where
let (attributes, children) =
join(self.attributes.resolve(), self.children.resolve()).await;
HtmlElement {
#[cfg(debug_assertions)]
defined_at: self.defined_at,
tag: self.tag,
attributes,
children,
}
@@ -336,6 +350,11 @@ where
cursor: &Cursor,
position: &PositionState,
) -> Self::State {
#[cfg(debug_assertions)]
{
set_currently_hydrating(Some(self.defined_at));
}
// non-Static custom elements need special support in templates
// because they haven't been inserted type-wise
if E::TAG.is_empty() && !FROM_SERVER {
@@ -349,7 +368,9 @@ where
cursor.sibling();
}
let el = crate::renderer::types::Element::cast_from(cursor.current())
.unwrap();
.unwrap_or_else(|| {
failed_to_cast_element(E::TAG, cursor.current())
});
let attrs = self.attributes.hydrate::<FROM_SERVER>(&el);

View File

@@ -128,7 +128,9 @@ where
}
/// Any type that can be added to the `style` attribute or set as a style in
/// the [`CssStyleDeclaration`]. This could be a plain string, or a property name-value pair.
/// the [`CssStyleDeclaration`](web_sys::CssStyleDeclaration).
///
/// This could be a plain string, or a property name-value pair.
pub trait IntoStyle: Send {
/// The type after all async data have resolved.
type AsyncOutput: IntoStyle;
@@ -606,7 +608,7 @@ impl<'a> IntoStyle for (&'a str, String) {
}
#[cfg(feature = "nightly")]
impl<'a, const V: &'static str> IntoStyle for (&'a str, Static<V>) {
impl<const V: &'static str> IntoStyle for (&str, Static<V>) {
type AsyncOutput = Self;
type State = ();
type Cloneable = (Arc<str>, Static<V>);

View File

@@ -2,7 +2,10 @@ use crate::{
renderer::{CastFrom, Rndr},
view::{Position, PositionState},
};
use std::{cell::RefCell, rc::Rc};
#[cfg(debug_assertions)]
use std::cell::Cell;
use std::{cell::RefCell, panic::Location, rc::Rc};
use web_sys::{Comment, Element, Node, Text};
/// Hydration works by walking over the DOM, adding interactivity as needed.
///
@@ -95,13 +98,121 @@ where
}
let marker = self.current();
position.set(Position::NextChild);
crate::renderer::types::Placeholder::cast_from(marker)
.expect("could not convert current node into marker node")
/*let marker2 = marker.clone();
Rndr::Placeholder::cast_from(marker).unwrap_or_else(|| {
crate::dom::log("expecting to find a marker. instead, found");
Rndr::log_node(&marker2);
panic!("oops.");
})*/
crate::renderer::types::Placeholder::cast_from(marker.clone())
.unwrap_or_else(|| failed_to_cast_marker_node(marker))
}
}
#[cfg(debug_assertions)]
thread_local! {
static CURRENTLY_HYDRATING: Cell<Option<&'static Location<'static>>> = const { Cell::new(None) };
}
pub(crate) fn set_currently_hydrating(
location: Option<&'static Location<'static>>,
) {
#[cfg(debug_assertions)]
{
CURRENTLY_HYDRATING.set(location);
}
#[cfg(not(debug_assertions))]
{
_ = location;
}
}
pub(crate) fn failed_to_cast_element(tag_name: &str, node: Node) -> Element {
#[cfg(not(debug_assertions))]
{
_ = node;
unreachable!();
}
#[cfg(debug_assertions)]
{
let hydrating = CURRENTLY_HYDRATING
.take()
.map(|n| n.to_string())
.unwrap_or_else(|| "{unknown}".to_string());
web_sys::console::error_3(
&wasm_bindgen::JsValue::from_str(&format!(
"A hydration error occurred while trying to hydrate an \
element defined at {hydrating}.\n\nThe framework expected an \
HTML <{tag_name}> element, but found this instead: ",
)),
&node,
&wasm_bindgen::JsValue::from_str(
"\n\nThe hydration mismatch may have occurred slightly \
earlier, but this is the first time the framework found a \
node of an unexpected type.",
),
);
panic!(
"Unrecoverable hydration error. Please read the error message \
directly above this for more details."
);
}
}
pub(crate) fn failed_to_cast_marker_node(node: Node) -> Comment {
#[cfg(not(debug_assertions))]
{
_ = node;
unreachable!();
}
#[cfg(debug_assertions)]
{
let hydrating = CURRENTLY_HYDRATING
.take()
.map(|n| n.to_string())
.unwrap_or_else(|| "{unknown}".to_string());
web_sys::console::error_3(
&wasm_bindgen::JsValue::from_str(&format!(
"A hydration error occurred while trying to hydrate an \
element defined at {hydrating}.\n\nThe framework expected a \
marker node, but found this instead: ",
)),
&node,
&wasm_bindgen::JsValue::from_str(
"\n\nThe hydration mismatch may have occurred slightly \
earlier, but this is the first time the framework found a \
node of an unexpected type.",
),
);
panic!(
"Unrecoverable hydration error. Please read the error message \
directly above this for more details."
);
}
}
pub(crate) fn failed_to_cast_text_node(node: Node) -> Text {
#[cfg(not(debug_assertions))]
{
_ = node;
unreachable!();
}
#[cfg(debug_assertions)]
{
let hydrating = CURRENTLY_HYDRATING
.take()
.map(|n| n.to_string())
.unwrap_or_else(|| "{unknown}".to_string());
web_sys::console::error_3(
&wasm_bindgen::JsValue::from_str(&format!(
"A hydration error occurred while trying to hydrate an \
element defined at {hydrating}.\n\nThe framework expected a \
text node, but found this instead: ",
)),
&node,
&wasm_bindgen::JsValue::from_str(
"\n\nThe hydration mismatch may have occurred slightly \
earlier, but this is the first time the framework found a \
node of an unexpected type.",
),
);
panic!(
"Unrecoverable hydration error. Please read the error message \
directly above this for more details."
);
}
}

View File

@@ -22,10 +22,17 @@ macro_rules! mathml_global {
At: NextTuple,
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
{
let HtmlElement { tag, children, attributes } = self;
let HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes
} = self;
HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
}
@@ -46,10 +53,11 @@ macro_rules! mathml_elements {
{
HtmlElement {
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
tag: [<$tag:camel>],
attributes: (),
children: (),
}
}
@@ -84,10 +92,17 @@ macro_rules! mathml_elements {
At: NextTuple,
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
{
let HtmlElement { tag, children, attributes } = self;
HtmlElement {
let HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes
} = self;
HtmlElement {
#[cfg(debug_assertions)]
defined_at,
tag,
children,
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
}

View File

@@ -1,12 +1,17 @@
use crate::html::{element::ElementType, node_ref::NodeRefContainer};
use reactive_graph::{
effect::Effect,
signal::{
guards::{Derefable, ReadGuard},
RwSignal,
},
traits::{DefinedAt, ReadUntracked, Set, Track},
traits::{
DefinedAt, Get, Notify, ReadUntracked, Set, Track, UntrackableGuard,
Write,
},
};
use send_wrapper::SendWrapper;
use std::{cell::Cell, ops::DerefMut};
use wasm_bindgen::JsCast;
/// A reactive reference to a DOM node that can be used with the `node_ref` attribute.
@@ -26,6 +31,25 @@ where
pub fn new() -> Self {
Self(RwSignal::new(None))
}
/// Runs the provided closure when the `NodeRef` has been connected
/// with its element.
#[inline(always)]
pub fn on_load<F>(self, f: F)
where
E: 'static,
F: FnOnce(E::Output) + 'static,
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
let f = Cell::new(Some(f));
Effect::new(move |_| {
if let Some(node_ref) = self.get() {
f.take().unwrap()(node_ref);
}
});
}
}
impl<E> Default for NodeRef<E>
@@ -78,6 +102,34 @@ where
}
}
impl<E> Notify for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
fn notify(&self) {
self.0.notify();
}
}
impl<E> Write for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
type Value = Option<SendWrapper<E::Output>>;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
self.0.try_write()
}
fn try_write_untracked(
&self,
) -> Option<impl DerefMut<Target = Self::Value>> {
self.0.try_write_untracked()
}
}
impl<E> ReadUntracked for NodeRef<E>
where
E: ElementType,

View File

@@ -1,6 +1,6 @@
#![allow(missing_docs)]
//! See [`Renderer`](super::Renderer) and [`Rndr`](super::Rndr) for additional information.
//! See [`Renderer`](crate::renderer::Renderer) and [`Rndr`](crate::renderer::Rndr) for additional information.
use super::{CastFrom, RemoveEventHandler};
use crate::{
@@ -15,7 +15,7 @@ use std::{any::TypeId, borrow::Cow, cell::RefCell};
use wasm_bindgen::{intern, prelude::Closure, JsCast, JsValue};
use web_sys::{Comment, HtmlTemplateElement};
/// A [`Renderer`] that uses `web-sys` to manipulate DOM elements in the browser.
/// A [`Renderer`](crate::renderer::Renderer) that uses `web-sys` to manipulate DOM elements in the browser.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Dom;

View File

@@ -14,7 +14,8 @@ pub mod dom;
/// applications, so this "generic rendering" approach was removed before 0.7.0 release.
///
/// It is possible that we will try a different approach to achieve the same functionality in the
/// future, so to the extent possible the rest of the crate tries to stick to using [`Renderer`]
/// future, so to the extent possible the rest of the crate tries to stick to using
/// [`Renderer`].
/// methods rather than directly manipulating the DOM inline.
pub type Rndr = dom::Dom;
@@ -209,7 +210,6 @@ pub trait DomRenderer: Renderer {
/// This works in a similar way to `TryFrom`. We implement it as a separate trait
/// simply so we don't have to create wrappers for the `web_sys` types; it can't be
/// implemented on them directly because of the orphan rules.
pub trait CastFrom<T>
where
Self: Sized,

View File

@@ -14,15 +14,16 @@ macro_rules! svg_elements {
/// An SVG element.
// `tag()` function
#[allow(non_snake_case)]
#[track_caller]
pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
where
{
HtmlElement {
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
tag: [<$tag:camel>],
attributes: (),
children: (),
}
}
@@ -153,9 +154,12 @@ svg_elements![
/// An SVG element.
#[allow(non_snake_case)]
#[track_caller]
pub fn r#use() -> HtmlElement<Use, (), ()>
where {
HtmlElement {
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
tag: Use,
attributes: (),
children: (),

View File

@@ -29,9 +29,6 @@ pub mod template;
pub mod tuples;
/// The `Render` trait allows rendering something as part of the user interface.
///
/// It is generic over the renderer itself, as long as that implements the [`Renderer`]
/// trait.
pub trait Render: Sized {
/// The “view state” for this type, which can be retained between updates.
///
@@ -197,7 +194,6 @@ where
/// Renders a view to an out-of-order stream of HTML with branch markers. This can be used to support libraries that diff
/// HTML pages against one another, by marking sections of the view that branch to different
/// types with marker comments.
fn to_html_stream_out_of_order_branching(self) -> StreamBuilder
where
Self: Sized,
@@ -372,7 +368,7 @@ pub trait ToTemplate {
/// The `style` attribute content known at compile time.
const STYLE: &'static str = "";
/// The length of the template.
const LEN: usize = Self::TEMPLATE.as_bytes().len();
const LEN: usize = Self::TEMPLATE.len();
/// Renders a view type to a template. This does not take actual view data,
/// but can be used for constructing part of an HTML `<template>` that corresponds

View File

@@ -100,8 +100,8 @@ macro_rules! render_primitive {
}
let node = cursor.current();
let node = crate::renderer::types::Text::cast_from(node)
.expect("couldn't cast text node from node");
let node = crate::renderer::types::Text::cast_from(node.clone())
.unwrap_or_else(|| crate::hydration::failed_to_cast_text_node(node));
if !FROM_SERVER {
Rndr::set_text(&node, &self.to_string());

View File

@@ -36,7 +36,7 @@ impl<'a> Render for &'a str {
}
}
impl<'a> RenderHtml for &'a str {
impl RenderHtml for &str {
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
@@ -90,8 +90,10 @@ impl<'a> RenderHtml for &'a str {
}
let node = cursor.current();
let node = crate::renderer::types::Text::cast_from(node)
.expect("couldn't cast text node from node");
let node = crate::renderer::types::Text::cast_from(node.clone())
.unwrap_or_else(|| {
crate::hydration::failed_to_cast_text_node(node)
});
if !FROM_SERVER {
Rndr::set_text(&node, self);
@@ -102,7 +104,7 @@ impl<'a> RenderHtml for &'a str {
}
}
impl<'a> ToTemplate for &'a str {
impl ToTemplate for &str {
const TEMPLATE: &'static str = " <!>";
fn to_template(
@@ -120,7 +122,7 @@ impl<'a> ToTemplate for &'a str {
}
}
impl<'a> Mountable for StrState<'a> {
impl Mountable for StrState<'_> {
fn unmount(&mut self) {
self.node.unmount()
}
@@ -451,7 +453,7 @@ impl<'a> Render for Cow<'a, str> {
}
}
impl<'a> RenderHtml for Cow<'a, str> {
impl RenderHtml for Cow<'_, str> {
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
@@ -494,7 +496,7 @@ impl<'a> RenderHtml for Cow<'a, str> {
}
}
impl<'a> ToTemplate for Cow<'a, str> {
impl ToTemplate for Cow<'_, str> {
const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;
fn to_template(
@@ -510,7 +512,7 @@ impl<'a> ToTemplate for Cow<'a, str> {
}
}
impl<'a> Mountable for CowStrState<'a> {
impl Mountable for CowStrState<'_> {
fn unmount(&mut self) {
self.node.unmount()
}