Compare commits

...

220 Commits
4088 ... 4285

Author SHA1 Message Date
Greg Johnston
6f047a2271 test: add regression test for #4296 2025-09-16 16:22:42 -04:00
Greg Johnston
7c942b8b47 chore: correct name for test 2025-09-16 16:07:00 -04:00
Greg Johnston
d4bf6d9cb6 test: add regression test for #4285 2025-09-15 21:05:12 -04:00
Greg Johnston
9deb96ea01 fix: provide correct URL/query/params to preloaders (closes #4296) 2025-09-15 19:46:52 -04:00
Greg Johnston
d1899cde1c during SSR, don't dispose of preload owners until whole request is done 2025-09-15 18:54:11 -04:00
Greg Johnston
ee731d7a3a fix: create individual owners for each preload 2025-09-12 18:51:46 -04:00
Greg Johnston
59cbcfa0fb fix: prevent infinite rebuild loop 2025-09-12 18:00:13 -04:00
Greg Johnston
0939cf63ad Revert "fix: prevent double-rebuild and correctly navigate multiple times to same lazy route (closes #4285)"
This reverts commit d37512bebd.
2025-09-12 17:59:14 -04:00
Greg Johnston
d37512bebd fix: prevent double-rebuild and correctly navigate multiple times to same lazy route (closes #4285) 2025-09-12 17:20:52 -04:00
Raffaele Fontana
3eaabf85ea docs: fix broken link in suspense (#4276) 2025-09-03 08:34:08 -04:00
Greg Johnston
d60c632c90 fix: correctly propagate visibility on lazy functions (closes #4256) (#4259) 2025-09-03 08:33:38 -04:00
Greg Johnston
f5ad4f4b88 fix: clear old attributes when replacing a Vec<AnyAttribute> (closes #4268) (#4270) 2025-09-01 07:41:54 -04:00
zakstucke
f3a053f99b feat: standardize ScopedFuture::new_untracked like untrack() and untrack_with_diagnostics() (#4269) 2025-08-31 12:30:16 -04:00
Greg Johnston
06573cbca1 Merge pull request #4272 from leptos-rs/4271
fix: revert changes to raw text parsing (closes #4271)
2025-08-30 11:25:13 -04:00
autofix-ci[bot]
9f4d826533 [autofix.ci] apply automated fixes 2025-08-30 11:25:00 +00:00
Greg Johnston
a305ae7227 chore: update leptos_macro version 2025-08-30 06:53:42 -04:00
Greg Johnston
65557c5723 leptos_macro-v0.8.8 2025-08-30 06:47:57 -04:00
Greg Johnston
a529f87ee2 Revert "fix: correctly parse unquoted text with punctuation in stable (closes #4137) (#4238)"
This reverts commit 99c3d8f9e9.
2025-08-30 06:46:36 -04:00
zakstucke
c0ca97e42f feat: impl From<RwSignal/ReadSignal/Memo> for ArcSignal (#4258) 2025-08-29 16:09:41 -04:00
dependabot[bot]
9a4e93ab07 chore(deps): bump the rust-dependencies group across 1 directory with 33 updates (#4262)
Bumps the rust-dependencies group with 32 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [serde_json](https://github.com/serde-rs/json) | `1.0.142` | `1.0.143` |
| [typed-builder](https://github.com/idanarye/rust-typed-builder) | `0.21.0` | `0.21.2` |
| [thiserror](https://github.com/dtolnay/thiserror) | `2.0.12` | `2.0.16` |
| [indexmap](https://github.com/indexmap-rs/indexmap) | `2.10.0` | `2.11.0` |
| [cfg-if](https://github.com/rust-lang/cfg-if) | `1.0.1` | `1.0.3` |
| [proc-macro2](https://github.com/dtolnay/proc-macro2) | `1.0.96` | `1.0.101` |
| [syn](https://github.com/dtolnay/syn) | `2.0.104` | `2.0.106` |
| [async-trait](https://github.com/dtolnay/async-trait) | `0.1.88` | `0.1.89` |
| [anyhow](https://github.com/dtolnay/anyhow) | `1.0.98` | `1.0.99` |
| [prettyplease](https://github.com/dtolnay/prettyplease) | `0.2.36` | `0.2.37` |
| [inventory](https://github.com/dtolnay/inventory) | `0.3.20` | `0.3.21` |
| [config](https://github.com/rust-cli/config-rs) | `0.15.13` | `0.15.14` |
| [regex](https://github.com/rust-lang/regex) | `1.11.1` | `1.11.2` |
| [tempfile](https://github.com/Stebalien/tempfile) | `3.20.0` | `3.21.0` |
| [percent-encoding](https://github.com/servo/rust-url) | `2.3.1` | `2.3.2` |
| [hyper](https://github.com/hyperium/hyper) | `1.6.0` | `1.7.0` |
| [reqwest](https://github.com/seanmonstar/reqwest) | `0.12.22` | `0.12.23` |
| [actix-http](https://github.com/actix/actix-web) | `3.11.0` | `3.11.1` |
| [bitflags](https://github.com/bitflags/bitflags) | `2.9.1` | `2.9.3` |
| [brotli](https://github.com/dropbox/rust-brotli) | `8.0.1` | `8.0.2` |
| [cc](https://github.com/rust-lang/cc-rs) | `1.2.32` | `1.2.34` |
| [form_urlencoded](https://github.com/servo/rust-url) | `1.2.1` | `1.2.2` |
| [idna](https://github.com/servo/rust-url) | `1.0.3` | `1.1.0` |
| [io-uring](https://github.com/tokio-rs/io-uring) | `0.7.9` | `0.7.10` |
| [jobserver](https://github.com/rust-lang/jobserver-rs) | `0.1.33` | `0.1.34` |
| [regex-automata](https://github.com/rust-lang/regex) | `0.4.9` | `0.4.10` |
| [regex-lite](https://github.com/rust-lang/regex) | `0.1.6` | `0.1.7` |
| [regex-syntax](https://github.com/rust-lang/regex) | `0.8.5` | `0.8.6` |
| [scc](https://github.com/wvwwvwwv/scalable-concurrent-containers) | `2.3.4` | `2.4.0` |
| [tinyvec](https://github.com/Lokathor/tinyvec) | `1.9.0` | `1.10.0` |
| [winapi-util](https://github.com/BurntSushi/winapi-util) | `0.1.9` | `0.1.10` |
| [winnow](https://github.com/winnow-rs/winnow) | `0.7.12` | `0.7.13` |



Updates `serde_json` from 1.0.142 to 1.0.143
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.142...v1.0.143)

Updates `typed-builder` from 0.21.0 to 0.21.2
- [Release notes](https://github.com/idanarye/rust-typed-builder/releases)
- [Changelog](https://github.com/idanarye/rust-typed-builder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/idanarye/rust-typed-builder/compare/v0.21.0...v0.21.2)

Updates `thiserror` from 2.0.12 to 2.0.16
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.12...2.0.16)

Updates `indexmap` from 2.10.0 to 2.11.0
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.10.0...2.11.0)

Updates `cfg-if` from 1.0.1 to 1.0.3
- [Release notes](https://github.com/rust-lang/cfg-if/releases)
- [Changelog](https://github.com/rust-lang/cfg-if/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cfg-if/compare/v1.0.1...v1.0.3)

Updates `proc-macro2` from 1.0.96 to 1.0.101
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.96...1.0.101)

Updates `syn` from 2.0.104 to 2.0.106
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.104...2.0.106)

Updates `async-trait` from 0.1.88 to 0.1.89
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/compare/0.1.88...0.1.89)

Updates `typed-builder-macro` from 0.21.0 to 0.21.2
- [Release notes](https://github.com/idanarye/rust-typed-builder/releases)
- [Changelog](https://github.com/idanarye/rust-typed-builder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/idanarye/rust-typed-builder/compare/v0.21.0...v0.21.2)

Updates `anyhow` from 1.0.98 to 1.0.99
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.98...1.0.99)

Updates `prettyplease` from 0.2.36 to 0.2.37
- [Release notes](https://github.com/dtolnay/prettyplease/releases)
- [Commits](https://github.com/dtolnay/prettyplease/compare/0.2.36...0.2.37)

Updates `inventory` from 0.3.20 to 0.3.21
- [Release notes](https://github.com/dtolnay/inventory/releases)
- [Commits](https://github.com/dtolnay/inventory/compare/0.3.20...0.3.21)

Updates `config` from 0.15.13 to 0.15.14
- [Changelog](https://github.com/rust-cli/config-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/config-rs/compare/v0.15.13...v0.15.14)

Updates `regex` from 1.11.1 to 1.11.2
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.1...1.11.2)

Updates `tempfile` from 3.20.0 to 3.21.0
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/commits)

Updates `percent-encoding` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/commits)

Updates `hyper` from 1.6.0 to 1.7.0
- [Release notes](https://github.com/hyperium/hyper/releases)
- [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper/compare/v1.6.0...v1.7.0)

Updates `reqwest` from 0.12.22 to 0.12.23
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.22...v0.12.23)

Updates `actix-http` from 3.11.0 to 3.11.1
- [Release notes](https://github.com/actix/actix-web/releases)
- [Changelog](https://github.com/actix/actix-web/blob/master/CHANGES.md)
- [Commits](https://github.com/actix/actix-web/compare/http-v3.11.0...http-v3.11.1)

Updates `bitflags` from 2.9.1 to 2.9.3
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.9.1...2.9.3)

Updates `brotli` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/dropbox/rust-brotli/releases)
- [Commits](https://github.com/dropbox/rust-brotli/commits/8.0.2)

Updates `cc` from 1.2.32 to 1.2.34
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.34)

Updates `form_urlencoded` from 1.2.1 to 1.2.2
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v1.2.1...v1.2.2)

Updates `idna` from 1.0.3 to 1.1.0
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/commits)

Updates `io-uring` from 0.7.9 to 0.7.10
- [Commits](https://github.com/tokio-rs/io-uring/commits)

Updates `jobserver` from 0.1.33 to 0.1.34
- [Commits](https://github.com/rust-lang/jobserver-rs/compare/0.1.33...0.1.34)

Updates `regex-automata` from 0.4.9 to 0.4.10
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/regex-automata-0.4.9...regex-automata-0.4.10)

Updates `regex-lite` from 0.1.6 to 0.1.7
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/regex-lite-0.1.6...regex-lite-0.1.7)

Updates `regex-syntax` from 0.8.5 to 0.8.6
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/regex-syntax-0.8.5...regex-syntax-0.8.6)

Updates `scc` from 2.3.4 to 2.4.0
- [Changelog](https://github.com/wvwwvwwv/scalable-concurrent-containers/blob/main/CHANGELOG.md)
- [Commits](https://github.com/wvwwvwwv/scalable-concurrent-containers/commits)

Updates `tinyvec` from 1.9.0 to 1.10.0
- [Changelog](https://github.com/Lokathor/tinyvec/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Lokathor/tinyvec/compare/v1.9.0...v1.10.0)

Updates `winapi-util` from 0.1.9 to 0.1.10
- [Commits](https://github.com/BurntSushi/winapi-util/compare/0.1.9...0.1.10)

Updates `winnow` from 0.7.12 to 0.7.13
- [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md)
- [Commits](https://github.com/winnow-rs/winnow/compare/v0.7.12...v0.7.13)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-version: 1.0.143
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: typed-builder
  dependency-version: 0.21.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: thiserror
  dependency-version: 2.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: indexmap
  dependency-version: 2.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: cfg-if
  dependency-version: 1.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: proc-macro2
  dependency-version: 1.0.101
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: syn
  dependency-version: 2.0.106
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: async-trait
  dependency-version: 0.1.89
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: typed-builder-macro
  dependency-version: 0.21.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: anyhow
  dependency-version: 1.0.99
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: prettyplease
  dependency-version: 0.2.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: inventory
  dependency-version: 0.3.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: config
  dependency-version: 0.15.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: regex
  dependency-version: 1.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: tempfile
  dependency-version: 3.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: percent-encoding
  dependency-version: 2.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: hyper
  dependency-version: 1.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: reqwest
  dependency-version: 0.12.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: actix-http
  dependency-version: 3.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: bitflags
  dependency-version: 2.9.3
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: brotli
  dependency-version: 8.0.2
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: cc
  dependency-version: 1.2.34
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: form_urlencoded
  dependency-version: 1.2.2
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: idna
  dependency-version: 1.1.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: io-uring
  dependency-version: 0.7.10
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: jobserver
  dependency-version: 0.1.34
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: regex-automata
  dependency-version: 0.4.10
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: regex-lite
  dependency-version: 0.1.7
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: regex-syntax
  dependency-version: 0.8.6
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: scc
  dependency-version: 2.4.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: tinyvec
  dependency-version: 1.10.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: winapi-util
  dependency-version: 0.1.10
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: winnow
  dependency-version: 0.7.13
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-29 16:08:39 -04:00
zakstucke
bee2b5ea1c feat: impl From<View<C>> for ViewFn where View<C>: Clone (#4266) 2025-08-29 16:02:00 -04:00
Spencer Ferris
3b058e77f1 fix: set Content-Type header for server function errors (closes #4209) (#4215) 2025-08-29 08:56:26 -04:00
zakstucke
7adb11ec49 Preserve owner in Actions and event listeners (#4267)
* Preserve Owner for Action's, allowing context retrieval

* Preserve owner for event listeners, allowing context retrieval

* CI

* Refactor

---------

Co-authored-by: Zak Stucke <zakstucke@hotmail.c.uk>
2025-08-29 11:50:33 +12:00
Greg Johnston
1af5f66ee6 fix: when navigating, only add new URL to history stack if it's different from current URL (closes #4251) (#4252) 2025-08-26 21:08:00 -04:00
Gabriel Lopes Veiga
956f1836ec fix: use wss for live reload if on https (#4257) 2025-08-26 21:07:28 -04:00
Greg Johnston
b54f80f529 chore: publish new patch releases for changed packages 2025-08-26 17:13:21 -04:00
Greg Johnston
a48a2994ee Merge pull request #4255 from leptos-rs/4254
Revert recent broken changes
2025-08-26 17:09:01 -04:00
Greg Johnston
aedcd5148c Revert "feat: add default "auto" live reload protocol option (#4224)"
This reverts commit a97eceacf1.
2025-08-26 13:25:49 -04:00
Greg Johnston
9160d8aaa6 Revert "made <Show> accept signals in addition to closures (#4236)"
This reverts commit db9f323f8d.
2025-08-26 13:24:44 -04:00
Greg Johnston
274fe07dae Merge remote-tracking branch 'origin' 2025-08-26 13:23:17 -04:00
Greg Johnston
7add26fc41 docs: correctly document additional serialization features for leptos_server (#4250) 2025-08-25 20:46:40 -04:00
Greg Johnston
d9213850f7 chore: publish new patch releases for changed packages 2025-08-25 20:40:32 -04:00
Marc-Stefan Cassola
db9f323f8d made <Show> accept signals in addition to closures (#4236) 2025-08-25 14:50:20 -07:00
Greg Johnston
1d0f668dc3 Merge pull request #4235 from leptos-rs/4217
Special-case `value` property to support quirky `<select>` behavior
2025-08-25 09:05:29 -04:00
Gabriel Lopes Veiga
a97eceacf1 feat: add default "auto" live reload protocol option (#4224)
* feat: add default "auto" live reload protocol option

* fix: make "auto" live reload protocol option last
2025-08-23 17:29:22 -07:00
Greg Johnston
3d6ea6d285 Merge pull request #4242 from leptos-rs/4239
Fixes for two server function issues
2025-08-22 21:00:54 -04:00
Greg Johnston
99c3d8f9e9 fix: correctly parse unquoted text with punctuation in stable (closes #4137) (#4238) 2025-08-22 21:00:39 -04:00
Greg Johnston
a394eb211f fix: transposed Accept/Content-Type headers in server function requests (closes #4240) 2025-08-22 16:32:42 -04:00
Greg Johnston
ceb7dd8ae5 fix: parse body rather than query string for PatchUrl and PutUrl (closes #4239) 2025-08-22 16:20:21 -04:00
Greg Johnston
f50adc00bc chore: rename changed method to avoid breaking user code that called set_property 2025-08-21 19:47:02 -04:00
Greg Johnston
1340deee96 chore: typo in name of feature test 2025-08-21 19:20:41 -04:00
Greg Johnston
8da3011a7f fix: special-case value prop so that it waits for children, if any, before being set (closes #4217) 2025-08-20 22:07:32 -04:00
Greg Johnston
959677f018 chore: move queue_microtask implementation into tachys 2025-08-20 22:06:38 -04:00
Greg Johnston
03529b3992 chore: add regression test for #4005 2025-08-20 21:19:35 -04:00
Greg Johnston
8bfd0ce143 chore: add regression test for #4217 2025-08-20 21:11:16 -04:00
Marc-Stefan Cassola
47199bbbf3 <ShowLet> component similar to <Show> but for Option (#4227)
* added the <Map> component

* chore: rustfmt

* removed support for `Result` from `<Map>` and added possibility to use
both closures and signals in the `value` prop.
2025-08-20 11:24:31 -07:00
Greg Johnston
9ed7e9de61 chore: remove lockfiles accidentally included in repo (#4234) 2025-08-20 10:34:42 -04:00
yescallop
26ecbf4df5 fix: allow non_snake_case and dead_code lints to run within component functions (#3198)
* fix: allow `non_snake_case` and `dead_code` lints to run within component functions

* Fixed component type name

* Update lib.rs

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Rakshith Ravi <rakshith.ravi@gmx.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-17 10:45:33 -07:00
dependabot[bot]
b3885c7be4 chore(deps): bump actions/checkout from 4 to 5 (#4221)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 11:39:37 -07:00
dependabot[bot]
436e5aa141 chore(deps): bump the rust-dependencies group with 32 updates (#4222)
Bumps the rust-dependencies group with 32 updates:

| Package | From | To |
| --- | --- | --- |
| [serde_json](https://github.com/serde-rs/json) | `1.0.141` | `1.0.142` |
| [trybuild](https://github.com/dtolnay/trybuild) | `1.0.106` | `1.0.110` |
| [tokio](https://github.com/tokio-rs/tokio) | `1.46.1` | `1.47.1` |
| [proc-macro2](https://github.com/dtolnay/proc-macro2) | `1.0.95` | `1.0.96` |
| [prettyplease](https://github.com/dtolnay/prettyplease) | `0.2.35` | `0.2.36` |
| [camino](https://github.com/camino-rs/camino) | `1.1.10` | `1.1.11` |
| [rkyv](https://github.com/rkyv/rkyv) | `0.8.10` | `0.8.11` |
| [uuid](https://github.com/uuid-rs/uuid) | `1.17.0` | `1.18.0` |
| [futures-lite](https://github.com/smol-rs/futures-lite) | `2.6.0` | `2.6.1` |
| [const-str](https://github.com/Nugine/const-str) | `0.6.3` | `0.6.4` |
| [postcard](https://github.com/jamesmunns/postcard) | `1.1.2` | `1.1.3` |
| [rustversion](https://github.com/dtolnay/rustversion) | `1.0.21` | `1.0.22` |
| [async-lock](https://github.com/smol-rs/async-lock) | `3.4.0` | `3.4.1` |
| [cc](https://github.com/rust-lang/cc-rs) | `1.2.30` | `1.2.32` |
| [cfg-expr](https://github.com/EmbarkStudios/cfg-expr) | `0.20.1` | `0.20.2` |
| [derive-where](https://github.com/ModProg/derive-where) | `1.5.0` | `1.6.0` |
| [event-listener](https://github.com/smol-rs/event-listener) | `5.4.0` | `5.4.1` |
| [glob](https://github.com/rust-lang/glob) | `0.3.2` | `0.3.3` |
| [hyper-util](https://github.com/hyperium/hyper-util) | `0.1.15` | `0.1.16` |
| [io-uring](https://github.com/tokio-rs/io-uring) | `0.7.8` | `0.7.9` |
| [libc](https://github.com/rust-lang/libc) | `0.2.174` | `0.2.175` |
| [munge](https://github.com/djkoloski/munge) | `0.4.5` | `0.4.6` |
| [munge_macro](https://github.com/djkoloski/munge) | `0.4.5` | `0.4.6` |
| redox_syscall | `0.5.15` | `0.5.17` |
| [rkyv_derive](https://github.com/rkyv/rkyv) | `0.8.10` | `0.8.11` |
| [rustc-demangle](https://github.com/rust-lang/rustc-demangle) | `0.1.25` | `0.1.26` |
| [rustls](https://github.com/rustls/rustls) | `0.23.29` | `0.23.31` |
| [signal-hook-registry](https://github.com/vorner/signal-hook) | `1.4.5` | `1.4.6` |
| [slab](https://github.com/tokio-rs/slab) | `0.4.10` | `0.4.11` |
| [tokio-util](https://github.com/tokio-rs/tokio) | `0.7.15` | `0.7.16` |
| [toml_parser](https://github.com/toml-rs/toml) | `1.0.1` | `1.0.2` |
| [zerovec](https://github.com/unicode-org/icu4x) | `0.11.2` | `0.11.4` |


Updates `serde_json` from 1.0.141 to 1.0.142
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.141...v1.0.142)

Updates `trybuild` from 1.0.106 to 1.0.110
- [Release notes](https://github.com/dtolnay/trybuild/releases)
- [Commits](https://github.com/dtolnay/trybuild/compare/1.0.106...1.0.110)

Updates `tokio` from 1.46.1 to 1.47.1
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.46.1...tokio-1.47.1)

Updates `proc-macro2` from 1.0.95 to 1.0.96
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.95...1.0.96)

Updates `prettyplease` from 0.2.35 to 0.2.36
- [Release notes](https://github.com/dtolnay/prettyplease/releases)
- [Commits](https://github.com/dtolnay/prettyplease/compare/0.2.35...0.2.36)

Updates `camino` from 1.1.10 to 1.1.11
- [Release notes](https://github.com/camino-rs/camino/releases)
- [Changelog](https://github.com/camino-rs/camino/blob/main/CHANGELOG.md)
- [Commits](https://github.com/camino-rs/camino/compare/camino-1.1.10...camino-1.1.11)

Updates `rkyv` from 0.8.10 to 0.8.11
- [Release notes](https://github.com/rkyv/rkyv/releases)
- [Commits](https://github.com/rkyv/rkyv/commits)

Updates `uuid` from 1.17.0 to 1.18.0
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.17.0...v1.18.0)

Updates `futures-lite` from 2.6.0 to 2.6.1
- [Release notes](https://github.com/smol-rs/futures-lite/releases)
- [Changelog](https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/futures-lite/compare/v2.6.0...v2.6.1)

Updates `const-str` from 0.6.3 to 0.6.4
- [Release notes](https://github.com/Nugine/const-str/releases)
- [Commits](https://github.com/Nugine/const-str/compare/v0.6.3...v0.6.4)

Updates `postcard` from 1.1.2 to 1.1.3
- [Release notes](https://github.com/jamesmunns/postcard/releases)
- [Commits](https://github.com/jamesmunns/postcard/compare/postcard/v1.1.2...postcard/v1.1.3)

Updates `rustversion` from 1.0.21 to 1.0.22
- [Release notes](https://github.com/dtolnay/rustversion/releases)
- [Commits](https://github.com/dtolnay/rustversion/compare/1.0.21...1.0.22)

Updates `async-lock` from 3.4.0 to 3.4.1
- [Release notes](https://github.com/smol-rs/async-lock/releases)
- [Changelog](https://github.com/smol-rs/async-lock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-lock/compare/v3.4.0...v3.4.1)

Updates `cc` from 1.2.30 to 1.2.32
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.32)

Updates `cfg-expr` from 0.20.1 to 0.20.2
- [Release notes](https://github.com/EmbarkStudios/cfg-expr/releases)
- [Changelog](https://github.com/EmbarkStudios/cfg-expr/blob/main/CHANGELOG.md)
- [Commits](https://github.com/EmbarkStudios/cfg-expr/compare/0.20.1...0.20.2)

Updates `derive-where` from 1.5.0 to 1.6.0
- [Release notes](https://github.com/ModProg/derive-where/releases)
- [Changelog](https://github.com/ModProg/derive-where/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ModProg/derive-where/compare/v1.5.0...v1.6.0)

Updates `event-listener` from 5.4.0 to 5.4.1
- [Release notes](https://github.com/smol-rs/event-listener/releases)
- [Changelog](https://github.com/smol-rs/event-listener/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/event-listener/compare/v5.4.0...v5.4.1)

Updates `glob` from 0.3.2 to 0.3.3
- [Release notes](https://github.com/rust-lang/glob/releases)
- [Changelog](https://github.com/rust-lang/glob/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/glob/compare/v0.3.2...v0.3.3)

Updates `hyper-util` from 0.1.15 to 0.1.16
- [Release notes](https://github.com/hyperium/hyper-util/releases)
- [Changelog](https://github.com/hyperium/hyper-util/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/hyper-util/compare/v0.1.15...v0.1.16)

Updates `io-uring` from 0.7.8 to 0.7.9
- [Commits](https://github.com/tokio-rs/io-uring/commits)

Updates `libc` from 0.2.174 to 0.2.175
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.175/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.174...0.2.175)

Updates `munge` from 0.4.5 to 0.4.6
- [Release notes](https://github.com/djkoloski/munge/releases)
- [Commits](https://github.com/djkoloski/munge/commits)

Updates `munge_macro` from 0.4.5 to 0.4.6
- [Release notes](https://github.com/djkoloski/munge/releases)
- [Commits](https://github.com/djkoloski/munge/commits)

Updates `redox_syscall` from 0.5.15 to 0.5.17

Updates `rkyv_derive` from 0.8.10 to 0.8.11
- [Release notes](https://github.com/rkyv/rkyv/releases)
- [Commits](https://github.com/rkyv/rkyv/commits)

Updates `rustc-demangle` from 0.1.25 to 0.1.26
- [Release notes](https://github.com/rust-lang/rustc-demangle/releases)
- [Changelog](https://github.com/rust-lang/rustc-demangle/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/rustc-demangle/commits/rustc-demangle-v0.1.26)

Updates `rustls` from 0.23.29 to 0.23.31
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.29...v/0.23.31)

Updates `signal-hook-registry` from 1.4.5 to 1.4.6
- [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vorner/signal-hook/compare/registry-v1.4.5...registry-v1.4.6)

Updates `slab` from 0.4.10 to 0.4.11
- [Release notes](https://github.com/tokio-rs/slab/releases)
- [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.11)

Updates `tokio-util` from 0.7.15 to 0.7.16
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-util-0.7.15...tokio-util-0.7.16)

Updates `toml_parser` from 1.0.1 to 1.0.2
- [Commits](https://github.com/toml-rs/toml/compare/toml_parser-v1.0.1...toml_parser-v1.0.2)

Updates `zerovec` from 0.11.2 to 0.11.4
- [Release notes](https://github.com/unicode-org/icu4x/releases)
- [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md)
- [Commits](https://github.com/unicode-org/icu4x/commits/ind/zerovec@0.11.4)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-version: 1.0.142
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: trybuild
  dependency-version: 1.0.110
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: tokio
  dependency-version: 1.47.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: proc-macro2
  dependency-version: 1.0.96
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: prettyplease
  dependency-version: 0.2.36
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: camino
  dependency-version: 1.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rkyv
  dependency-version: 0.8.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: uuid
  dependency-version: 1.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: futures-lite
  dependency-version: 2.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: const-str
  dependency-version: 0.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: postcard
  dependency-version: 1.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rustversion
  dependency-version: 1.0.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: async-lock
  dependency-version: 3.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: cc
  dependency-version: 1.2.32
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: cfg-expr
  dependency-version: 0.20.2
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: derive-where
  dependency-version: 1.6.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: event-listener
  dependency-version: 5.4.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: glob
  dependency-version: 0.3.3
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: hyper-util
  dependency-version: 0.1.16
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: io-uring
  dependency-version: 0.7.9
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: libc
  dependency-version: 0.2.175
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: munge
  dependency-version: 0.4.6
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: munge_macro
  dependency-version: 0.4.6
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: redox_syscall
  dependency-version: 0.5.17
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rkyv_derive
  dependency-version: 0.8.11
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rustc-demangle
  dependency-version: 0.1.26
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rustls
  dependency-version: 0.23.31
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: signal-hook-registry
  dependency-version: 1.4.6
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: slab
  dependency-version: 0.4.11
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: tokio-util
  dependency-version: 0.7.16
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: toml_parser
  dependency-version: 1.0.2
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: zerovec
  dependency-version: 0.11.4
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 11:39:20 -07:00
Greg Johnston
05cafa8b06 fix: support islands routing in 404 routes (#4218) 2025-08-11 21:46:10 -04:00
Greg Johnston
9e3c0cc402 fix: pass hydrate_async through OwnedView properly (closes #4219) (#4220) 2025-08-11 21:06:02 -04:00
Gabriel Lopes Veiga
30141293f6 feat: implement IntoProperty for Oco (#4174) 2025-08-09 15:34:51 -04:00
Saber Haj Rabiee
8f623a2d5b feat: improving the bump script (#4187) 2025-08-09 15:31:50 -04:00
Greg Johnston
f2fe791f6b perf: use a set instead of Vec<_> to optimize large subscriber sets (see #4138) (#4201) 2025-08-09 15:31:05 -04:00
Greg Johnston
30dbb7ccc8 fix: ensure task::spawn maintains reactive ownership (closes #4203) (#4206) 2025-08-09 15:30:23 -04:00
Aleksander Heintz
b986fe11dc make is_server and is_browser public (#4204) 2025-08-05 17:10:10 -07:00
mskorkowski
e2e28ef180 fix: allowing deriving Patch for a struct with generic argument (closes #4163) (#4175) 2025-08-03 08:28:42 -04:00
Adam Doyle
a5e0053bab chore: add name attribute to details element (#4190) 2025-08-03 08:24:54 -04:00
Greg Johnston
6c04a1cd76 fix: only continue navigating to most recent page (closes #4195) (#4198) 2025-08-03 08:24:19 -04:00
Raffaele Fontana
87fb947465 docs: fix typo (#4202) 2025-08-01 11:12:42 -04:00
mskorkowski
5ba818132a feat: add command and commandfor attributes for button (closes #4193) (#4194) 2025-07-31 16:45:38 -04:00
Greg Johnston
30b917cfc3 v0.8.6 2025-07-27 08:59:22 -04:00
Greg Johnston
6cd731cbb1 Merge pull request #4186 from leptos-rs/4184
A few pieces of lazy island clean-up
2025-07-27 08:50:22 -04:00
Greg Johnston
f1b6b79e27 Enhancing members’ versioning (#4172)
* fix: decouple versioning for members

* feat: handy script to bump changed member crates from the last released tag
2025-07-27 08:50:08 -04:00
Greg Johnston
623ee08f82 chore: this does not need to be async 2025-07-27 08:34:59 -04:00
Greg Johnston
877849a5dd chore: new wasm_split version 2025-07-27 08:30:51 -04:00
Greg Johnston
fb59da90c2 fix: support file hashing when using lazy loading (#4182) 2025-07-26 15:46:31 -04:00
Greg Johnston
d33f5c9e77 feat: allow lazy server functions (#4169) 2025-07-24 07:30:05 -04:00
dependabot[bot]
deb8e96eb0 chore(deps): bump redox_syscall in the rust-dependencies group (#4171)
Bumps the rust-dependencies group with 1 update: redox_syscall.


Updates `redox_syscall` from 0.5.14 to 0.5.15

---
updated-dependencies:
- dependency-name: redox_syscall
  dependency-version: 0.5.15
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 12:33:31 -04:00
Greg Johnston
181e4d0566 chore: bump wasm-split version numbers (#4170) 2025-07-22 12:31:46 -04:00
Saber Haj Rabiee
525379a9b3 fix(CI): remove autofix CI timeout (#4173) 2025-07-22 12:31:04 -04:00
Saber Haj Rabiee
783a233167 feat: handy script to bump changed member crates from the last released tag 2025-07-21 22:44:40 -07:00
Saber Haj Rabiee
8079956d1b fix: decouple versioning for members 2025-07-21 22:43:58 -07:00
Greg Johnston
f5d3fbb091 0.8.5 2025-07-21 09:04:16 -04:00
Greg Johnston
fbe7cdc482 docs: update documentation for #[lazy] and #[lazy_route] 2025-07-21 08:53:38 -04:00
Greg Johnston
14884bc8ac Merge pull request #3988 from leptos-rs/wasm-splitting-support
feat: wasm-splitting library support for future cargo-leptos integration
2025-07-21 07:17:29 -04:00
Greg Johnston
2c93e1a185 fix: avoid name conflict between lazy route struct type and split view function 2025-07-20 19:59:10 -04:00
Greg Johnston
64b8c3dfd5 fix: use dummy macro output to improve rust-analyzer experience for #[lazy_route] 2025-07-20 19:58:41 -04:00
martin frances
5f2d511553 chore: bump oco_ref version number (#4168) 2025-07-20 18:44:09 -04:00
Greg Johnston
d7cdc6c489 chore: fix Cargo.lock 2025-07-20 13:12:03 -04:00
Greg Johnston
ebb33b6f41 Merge remote-tracking branch 'origin' into wasm-splitting-support 2025-07-20 13:09:26 -04:00
Greg Johnston
809c0b532c chore: cargo update 2025-07-20 13:07:47 -04:00
Greg Johnston
b13f2420fb chore: change name of wasm_split due to namesquatting 2025-07-20 12:54:30 -04:00
Greg Johnston
77de264615 chore: publish wasm_split and wasm_split_macros 2025-07-20 12:54:14 -04:00
Greg Johnston
1524386346 v0.8.4 2025-07-20 11:11:29 -04:00
Greg Johnston
426b079709 Merge pull request #4167 from shadr/hot-reload-fixes
fix(hot-reload): hot-reload stops working when number of views changes in a file + small fixes
2025-07-20 11:00:05 -04:00
shadr
c6f176e2b0 Merge branch 'leptos-rs:main' into hot-reload-fixes 2025-07-20 16:23:48 +03:00
Greg Johnston
75662d08e7 Merge pull request #4162 from shadr/hot-reload-myers-diff
fix(hot-reload): implement Myers diffing algorithm
2025-07-20 08:23:58 -04:00
mahdi739
4448b77cde feat: add debug_log!, debug_error!, console_debug_log and console_debug_error (#4160) 2025-07-20 08:11:17 -04:00
Dylan Anthony
956af8e466 feat: allow using Actix without default features (#3921) 2025-07-20 08:03:49 -04:00
Nesterov Nikita
8c469b85d6 fix(hot-reload): ignore clippy::needless_range_loop lint 2025-07-20 07:49:26 +03:00
Saber Haj Rabiee
7f93dd224d fix(CI): check latest commit for version release instead of version tag (#4150) 2025-07-19 21:38:56 -04:00
martin frances
777b5e1e54 chore: examples - bumped version numbers for sqlx and this error. (#4126) 2025-07-19 21:19:32 -04:00
Greg Johnston
37cb102d53 fix: wait for preloaded route data as part of route transition 2025-07-19 13:46:30 -04:00
Nesterov Nikita
433f7284e6 fix(hot-reload): update view map when number of views mismatch 2025-07-19 16:25:33 +03:00
Greg Johnston
897e6ecc26 example: lazy routes in hackernews_axum 2025-07-19 08:53:02 -04:00
Greg Johnston
0c67f7d389 fix: properly support concurrent loading without breaking changes to ChooseView 2025-07-19 08:53:02 -04:00
Greg Johnston
232b603a25 feat: support both sync and async lazy functions 2025-07-19 08:53:02 -04:00
Nesterov Nikita
4a8a212d84 fix(hot-reload): ReplaceWith couldn't replace Fragment with an Element 2025-07-19 15:48:57 +03:00
Nesterov Nikita
1d7bc021af fix(hot-reload): ClearChildren couldn't clear fragment view 2025-07-19 15:46:18 +03:00
Nesterov Nikita
74055a7e13 fix(hot-reload): fix AppendChildren patch command 2025-07-19 15:06:13 +03:00
Nesterov Nikita
c98082de74 fix(hot-reload): insertion before/after fragment in a tag
Previous commits that aimed at fixing indexing for Myers algorithm broke
insertion before/after a fragment in a html tag, resulting in incorrect
ordering/error
2025-07-19 13:48:09 +03:00
autofix-ci[bot]
b8d44e20a9 [autofix.ci] apply automated fixes 2025-07-19 09:05:20 +00:00
Nesterov Nikita
00e83e0d70 fix(hot-reload): update InsertChild parent node logic 2025-07-19 10:12:58 +03:00
Nesterov Nikita
e89b1389ca fix(hot-reload): rebuild actual children before each patch 2025-07-19 08:05:48 +03:00
Nesterov Nikita
bd454d03e2 refactor(hot-reload): immediately apply patches 2025-07-19 08:05:11 +03:00
Nesterov Nikita
d7f4457ea4 feat(hot-reload): implement Myers diffing algorithm 2025-07-19 07:39:32 +03:00
Rakshith Ravi
17d357bcec chore(README): we're kinda prod-ready (#4148) 2025-07-18 10:33:45 -04:00
dependabot[bot]
66d1bead9a chore(deps): bump the rust-dependencies group across 1 directory with 15 updates (#4152)
---
updated-dependencies:
- dependency-name: trybuild
  dependency-version: 1.0.106
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: tokio
  dependency-version: 1.46.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: config
  dependency-version: 0.15.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: const-str
  dependency-version: 0.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: reqwest
  dependency-version: 0.12.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: cc
  dependency-version: 1.2.29
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: cfg-expr
  dependency-version: 0.20.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: collection_literals
  dependency-version: 1.0.2
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: crc32fast
  dependency-version: 1.5.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: h2
  dependency-version: 0.3.27
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: hyper-util
  dependency-version: 0.1.15
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rustls
  dependency-version: 0.23.29
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: rustls-webpki
  dependency-version: 0.103.4
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: sdd
  dependency-version: 3.0.9
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: winnow
  dependency-version: 0.7.12
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 10:32:18 -04:00
Greg Johnston
69c918e813 Merge pull request #4154 from shadr/hot-reload-fixes
fix: three hot-reloading bugs ( closes #3191 )
2025-07-18 10:31:18 -04:00
TERRORW0LF
2817a261ce docs: add warning for reading hash on the server (#4158) 2025-07-18 10:25:17 -04:00
mahdi739
972b1ff90b feat: support conversion from signals and optional get extension for TextProp (#4159)
* feat: support conversion from signals and optional get extension for TextProp

* [autofix.ci] apply automated fixes

* remove unused import

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-18 10:24:32 -04:00
autofix-ci[bot]
10c13bbca2 [autofix.ci] apply automated fixes 2025-07-18 13:01:09 +00:00
Greg Johnston
e545b7c48a chore: remove unnecessary lifetime 2025-07-18 08:31:30 -04:00
Greg Johnston
839eb9ac1c fix: correctly handle preloads when they do or don't exist 2025-07-18 08:30:02 -04:00
Greg Johnston
ae9324e555 fix: use crossorigin rather than nonce for <link rel="modulepreload"> 2025-07-18 08:29:34 -04:00
shadr
3a66a1f3d3 Merge branch 'main' into hot-reload-fixes 2025-07-18 06:05:14 +03:00
Greg Johnston
f7c4a664d2 chore(ci): add new wasm-splitting crates to CI 2025-07-17 19:11:11 -04:00
Greg Johnston
d446474456 chore: clippy 2025-07-17 19:10:03 -04:00
Greg Johnston
d7bc6715a6 chore: clippy 2025-07-17 19:09:32 -04:00
Greg Johnston
4c95cddca8 chore: cargo fmt 2025-07-17 14:19:52 -04:00
Greg Johnston
437d61bed7 chore: allow non-snake-case name on LazyRoute::view() 2025-07-17 14:17:14 -04:00
Greg Johnston
3fdbae4314 Merge remote-tracking branch 'origin' into wasm-splitting-support 2025-07-17 14:07:29 -04:00
Greg Johnston
7559b27361 feat: support preloading split WASM files from a manifest 2025-07-17 14:06:20 -04:00
Greg Johnston
b9bb14cfdc chore: clippy 2025-07-17 14:06:05 -04:00
Greg Johnston
504f983996 Merge pull request #4161 from leptos-rs/version-updates
Version updates + stable hot-reloading
2025-07-17 13:36:52 -04:00
Greg Johnston
c7a319db15 nested concurrent lazy routes 2025-07-17 11:21:05 -04:00
Greg Johnston
0862385816 fix: hot-reloading still only supported in debug 2025-07-17 09:29:29 -04:00
Greg Johnston
0d18da720b fix: rename arguments for lazy routes 2025-07-17 09:27:31 -04:00
Greg Johnston
12f5676bd1 fix/change: proper concurrent data loading for routes 2025-07-17 09:27:18 -04:00
Greg Johnston
0fa8155adc fix: correct async hydration for elements 2025-07-17 09:26:55 -04:00
Greg Johnston
f8fa6de987 preload lazy routes without creating data twice 2025-07-17 08:09:05 -04:00
Greg Johnston
81b37a3867 create a separate preload function for lazy functions 2025-07-17 08:08:56 -04:00
Greg Johnston
8319446d3f chore: update nightly error output for server function return types 2025-07-17 07:10:59 -04:00
Greg Johnston
efb1e945d9 hydrate lazy islands in correct order 2025-07-17 06:51:51 -04:00
Greg Johnston
5fa31941bb chore: remove unused type in test 2025-07-17 06:11:35 -04:00
Greg Johnston
f4bb87ea1e feat: support hot-reloading on stable 2025-07-16 21:34:32 -04:00
Greg Johnston
016fbf8da1 chore: bump nightly version in CI 2025-07-16 18:03:07 -04:00
Greg Johnston
21fd995468 change: set MSRV to 1.88 (proc-macro spans stabilized = stable hot-reloading) 2025-07-16 18:02:40 -04:00
Greg Johnston
17b9bec79a update test results 2025-07-16 18:01:19 -04:00
Greg Johnston
acd69daedb clippy 2025-07-16 17:40:45 -04:00
Greg Johnston
b8a3129396 support hot-reloading on stable 2025-07-16 17:34:05 -04:00
Greg Johnston
783b4c4b04 Merge remote-tracking branch 'origin' into wasm-splitting-support 2025-07-16 17:25:34 -04:00
Greg Johnston
683e7177dd docs: update README to remove nightly note, as it has not been the default for the examples/book for a long time 2025-07-16 17:25:28 -04:00
Greg Johnston
eede2e9e6c unnecessary unsafe that had no safety comment in the POC! 2025-07-16 17:23:42 -04:00
Greg Johnston
31d51ea94f clippy 2025-07-16 17:23:32 -04:00
Greg Johnston
01fbd82edf use more recent nightly in CI 2025-07-16 17:20:34 -04:00
Greg Johnston
b276e703a8 unblock hot reloading on stable now that proc-macro spans are stabilized 2025-07-16 17:20:15 -04:00
Greg Johnston
d2409a22a7 update MSRV to allow deduplicating lazy function names 2025-07-16 17:19:46 -04:00
Greg Johnston
f6cd784088 clippy 2025-07-16 17:18:01 -04:00
Greg Johnston
eb9ebc870f regression tests for #4157 and for https://github.com/leptos-rs/cargo-leptos/issues/546 2025-07-16 07:47:30 -04:00
Greg Johnston
b746c2ac4e feat: deduplicate lazy function names with a hash (closes #4157) 2025-07-16 07:46:40 -04:00
Greg Johnston
4c1e7dc8c1 add README 2025-07-15 20:43:29 -04:00
Greg Johnston
f1fa4635c7 clippy 2025-07-15 09:37:50 -04:00
Greg Johnston
46c8a11eae infrastructure for testing with --split 2025-07-15 09:37:45 -04:00
Greg Johnston
6b72ce3c16 Merge remote-tracking branch 'origin' into wasm-splitting-support 2025-07-15 09:10:50 -04:00
Nesterov Nikita
33b278c014 fix(hot-reload): fragments were not walked over properly 2025-07-15 09:35:06 +03:00
Nesterov Nikita
5fc56346f4 chore: format patch.js with prettier 2025-07-15 09:30:30 +03:00
Nesterov Nikita
afb37aaf4b fix(hot-reload): handle DOM-less views 2025-07-15 09:28:59 +03:00
Nesterov Nikita
f8fd79725a fix(hot-reload): parse RawText node 2025-07-15 09:28:19 +03:00
Greg Johnston
131251b361 fix: bump workspace dependency versions (closes #4146) (#4149) 2025-07-14 10:55:25 -04:00
Greg Johnston
91fb315fe0 v0.8.3 2025-07-12 20:10:21 -04:00
mskorkowski
6954b77b62 fix: generics on stores (closes #4136) (#4142)
Fixes the case when struct had a generic arguments by adding missing generic arguments into the generated trait and the said trait implementation.
2025-07-12 20:04:48 -04:00
Greg Johnston
299a4c161f clean up dependencies (see #3987) 2025-07-12 14:11:27 -04:00
Greg Johnston
b0ee946412 clean up example 2025-07-12 14:08:00 -04:00
Greg Johnston
b505892568 add lazy-routing example 2025-07-12 14:00:19 -04:00
Greg Johnston
b63cfa7935 support for lazy hydration 2025-07-12 13:22:12 -04:00
Greg Johnston
01a939e1e4 weird 2025-07-11 11:03:49 -04:00
Greg Johnston
995bc60c74 missing import 2025-07-11 11:00:57 -04:00
Greg Johnston
4c4869d33c support custom names for split functions 2025-07-11 10:59:06 -04:00
Greg Johnston
0ca8d32805 fix lazy_route 2025-07-11 10:53:30 -04:00
Greg Johnston
0d853fdb74 re-export lazy route macro 2025-07-11 10:37:18 -04:00
Greg Johnston
853f049d9f Merge remote-tracking branch 'origin' into wasm-splitting-support 2025-07-11 10:37:00 -04:00
Saber Haj Rabiee
77176f8395 fix(examples): remove redundant cf-worker example (#4140)
Cloudflare has an official template for leptos https://github.com/cloudflare/workers-rs/blob/main/templates/leptos
2025-07-11 10:35:14 -04:00
autofix-ci[bot]
84136cafa5 [autofix.ci] apply automated fixes 2025-07-10 16:36:07 +00:00
Greg Johnston
bb3f1deb1f Merge remote-tracking branch 'origin' into wasm-splitting-support 2025-07-10 12:15:45 -04:00
Greg Johnston
344b79a01b chore: fix cargo-leptos command in README (closes #4134) 2025-07-06 08:51:20 -04:00
Greg Johnston
051059c761 Merge pull request #4115 from leptos-rs/4114-fix
Clean up nested routing ownership and add regression tests
2025-07-01 08:32:52 -04:00
Ryo Hirayama
3c540dd858 Add an example to show server_fn is capable to serve on Cloudflare Workers (#4052)
* Add reqwest-no-ws feature to server_fn

* Add dep:tokio to server_fn/reqwest-no-ws

* Fix

* Refactor reqwest-no-ws feature in server_fn crate for wasm32 support

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* Ad cf-worker example

* Fix error messages for trybuild

* Revert "Fix error messages for trybuild"

This reverts commit 42658dd031.

* Fix CI error by disabling on reqwest-no-ws aslike other feature

* Compact deps and add ci

* Revert all server_fn changes as main

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-06-30 23:44:12 -07:00
Greg Johnston
4125688a0a fix: don't create an extra intermediate outlet (messes with context) 2025-06-30 16:55:19 -04:00
Greg Johnston
bd3b962cfb fix: dispose of all previous owners simultaneously when all routes are complete 2025-06-30 09:51:20 -04:00
Greg Johnston
5dd3c217c4 fix: don't dispose of view owners immediately when outlets rerun 2025-06-30 09:51:02 -04:00
Greg Johnston
ae00e5ae13 test: add regression test for nested context on server 2025-06-30 09:49:16 -04:00
Greg Johnston
1ce671ba08 test: fix signal disposal test 2025-06-30 09:46:22 -04:00
Greg Johnston
ec9f26bd9f chore: remove unused variable 2025-06-30 09:06:18 -04:00
Greg Johnston
831eae31bc fix: much better solution for nested route disposal 2025-06-30 09:05:22 -04:00
Greg Johnston
ff6ae5de25 test: add regression test for signal disposal issue 2025-06-30 08:49:25 -04:00
Greg Johnston
c21712ba04 chore: simplify element_by_id (see #4121) 2025-06-29 17:16:51 -04:00
Greg Johnston
45771b6fd3 fix: correctly rebuild AnyView when the current view doesn't appear in the DOM (closes #4122) 2025-06-29 17:10:32 -04:00
Greg Johnston
f3557970a7 fix: uses EXISTS to mark things that don't exist in the DOM 2025-06-29 17:10:05 -04:00
Greg Johnston
c87ef331b0 fix: fix: correctly construct child links during rebuild 2025-06-29 14:07:48 -04:00
martin frances
e767518142 chore: updated clippy rule affecting stores example (#4120)
status.done().then_some("line-through").unwrap_or_default()
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if status.done() { "line-through" } else { Default::default() }`
2025-06-28 14:53:27 -04:00
Greg Johnston
f94b681118 fix: correctly clear child route data 2025-06-28 14:31:57 -04:00
Greg Johnston
9c50e49253 test: add regression test for #4088 2025-06-28 14:15:49 -04:00
Greg Johnston
57c7097ede fix: disable InertElement when global class is provided (closes #4116) (#4119) 2025-06-28 13:53:58 -04:00
dependabot[bot]
1a06e0eee8 chore(deps): bump the rust-dependencies group across 1 directory with 18 updates (#4110)
---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.104
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: glib
  dependency-version: 0.20.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: prettyplease
  dependency-version: 0.2.35
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: autocfg
  dependency-version: 1.5.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: derive-where
  dependency-version: 1.5.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: errno
  dependency-version: 0.3.13
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: glib-macros
  dependency-version: 0.20.12
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: libc
  dependency-version: 0.2.174
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: munge
  dependency-version: 0.4.5
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: munge_macro
  dependency-version: 0.4.5
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: quinn-udp
  dependency-version: 0.5.13
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: r-efi
  dependency-version: 5.3.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: rust-dependencies
- dependency-name: rustls
  dependency-version: 0.23.28
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: slab
  dependency-version: 0.4.10
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: tracing-attributes
  dependency-version: 0.1.30
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: webpki-roots
  dependency-version: 1.0.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: zerocopy
  dependency-version: 0.8.26
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
- dependency-name: zerocopy-derive
  dependency-version: 0.8.26
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: rust-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-28 13:53:18 -04:00
bicarlsen
ce9af4a685 fix: use HTML-namespaced InertElement for top-level <svg> elements. (#4109) 2025-06-28 13:53:02 -04:00
martin frances
e0c79eb8d8 chore: bump syn and tokio-tungsenite. (#4117) 2025-06-28 13:52:20 -04:00
Greg Johnston
9fd972971e test: add regression test for back/forward behavior mentioned in #4114 2025-06-27 18:50:28 -04:00
Greg Johnston
9473220639 test: add regression test for #4015 2025-06-27 18:42:30 -04:00
Greg Johnston
ae11812dc6 fix: ensure cleanups run when navigating between sibling Routes in Outlet 2025-06-27 17:59:09 -04:00
Greg Johnston
4c55c25445 chore: clean up unused owner manipulation 2025-06-27 17:59:09 -04:00
Greg Johnston
649b5fbe9e Merge pull request #4114 from metatoaster/regression_examples
A place to put e2e tests for regression, plus reporting issue caused by #4091.
2025-06-27 08:28:20 -04:00
Tommy Yu
adb3e75efc test: e2e that demonstrates failure
- if b37900ec55 from #4091 is reverted the
  failure described in the `pr_4091.feature` will no longer happen.
2025-06-27 22:42:40 +12:00
Tommy Yu
f303aa6d5c test: leptos-rs/leptos#4091 regression 2025-06-27 22:42:40 +12:00
Tommy Yu
73ca3d7b04 Adding a basic regression example 2025-06-27 22:42:40 +12:00
Greg Johnston
235393bfbe chore: remove now-unused join_contexts API (#4113) 2025-06-26 08:58:01 -04:00
Greg Johnston
17d8e2bd09 fix: correctly provide context via nested outlets (closes #4088) (#4091) 2025-06-25 20:00:10 -04:00
Greg Johnston
5d9df592d5 fix: don't assume classList is unchanged when rebuilding a class effect for the first time (#3983 part two) 2025-05-21 21:53:50 -04:00
Greg Johnston
323de496f3 fix: don't use Arc::ptr_eq for string comparison (closes #3983) 2025-05-21 21:53:50 -04:00
Álvaro Mondéjar Rubio
c8df5b75ef fix: forward missing lint attributes passed to #[component] macro (#3989) 2025-05-21 21:53:50 -04:00
Dennis Waldherr
89cbf86595 docs: provide error message if file hashing is enabled but no hash file is present (#3990)
Co-authored-by: Dennis Waldherr <bytekeeper@mailbox.org>
2025-05-21 21:53:50 -04:00
mskorkowski
b78a6655f3 fix: smooshed static segments no longer matches the path #3968 (#3973)
* fix: smooshed static segments no longer matches the path #3968

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-05-21 21:53:50 -04:00
Greg Johnston
b5797ffe6a fix: meta tags not properly rendered inside synchronously-available Suspend (closes #3976) (#3991) 2025-05-21 21:53:50 -04:00
Greg Johnston
775e2eabed feat: #[island(lazy)] 2025-05-21 21:53:50 -04:00
autofix-ci[bot]
37405ec778 [autofix.ci] apply automated fixes 2025-05-21 20:32:35 +00:00
Greg Johnston
54890af875 add wasm-split workplace dependencies 2025-05-21 16:12:50 -04:00
Greg Johnston
5479ece865 Merge branch 'main' into wasm-splitting-support 2025-05-17 15:03:28 -04:00
Greg Johnston
f0b7e7445b feat: wasm-splitting library support for future cargo-leptos integration 2025-05-17 15:00:38 -04:00
208 changed files with 6260 additions and 1376 deletions

View File

@@ -17,11 +17,10 @@ env:
jobs:
autofix:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with: {toolchain: "nightly-2025-04-16", components: "rustfmt, clippy", target: "wasm32-unknown-unknown", rustflags: ""}
with: {toolchain: "nightly-2025-07-16", components: "rustfmt, clippy", target: "wasm32-unknown-unknown", rustflags: ""}
- name: Install Glib
run: |
sudo apt-get update

View File

@@ -63,6 +63,6 @@ jobs:
sudo apt-get update
sudo apt-get install -y libglib2.0-dev
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Semver Checks
uses: obi1kenobi/cargo-semver-checks-action@v2

View File

@@ -19,7 +19,7 @@ jobs:
matrix: ${{ steps.set-example-changed.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Get example files that changed

View File

@@ -17,7 +17,7 @@ jobs:
EXCLUDED_EXAMPLES: cargo-make
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install jq
run: sudo apt-get install jq
- name: Set Matrix

View File

@@ -13,7 +13,7 @@ jobs:
leptos_changed: ${{ steps.set-source-changed.outputs.leptos_changed }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Get source files that changed

View File

@@ -13,7 +13,7 @@ jobs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install jq
run: sudo apt-get install jq
- name: Set Matrix

View File

@@ -12,7 +12,7 @@ jobs:
contents: write # To push a branch
pull-requests: write # To create a PR from that branch
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install mdbook

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
toolchain: [stable, nightly-2025-04-16]
toolchain: [stable, nightly-2025-07-16]
erased_mode: [true, false]
steps:
- name: Free Disk Space
@@ -53,7 +53,7 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
@@ -169,7 +169,9 @@ jobs:
cd '${{ inputs.directory }}'
cargo make --no-workspace --profile=github-actions ci
# check the direct-minimal-versions on release
if [[ "${{ github.ref_name }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
COMMIT_MSG=$(git log -1 --pretty=format:'%s')
# Supports: v1.2.3, v1.2.3-alpha, v1.2.3-beta1, v1.2.3-rc.1, etc.
if [[ "$COMMIT_MSG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.?[0-9]+)?)?$ ]]; then
cargo make --no-workspace --profile=github-actions check-minimal-versions
fi
# Check if the counter_isomorphic can be built with leptos_debuginfo cfg flag in release mode

806
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -40,47 +40,49 @@ members = [
exclude = ["benchmarks", "examples", "projects"]
[workspace.package]
version = "0.8.2"
edition = "2021"
rust-version = "1.80"
rust-version = "1.88"
[workspace.dependencies]
# members
throw_error = { path = "./any_error/", version = "0.3.0" }
any_spawner = { path = "./any_spawner/", version = "0.3.0" }
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1" }
either_of = { path = "./either_of/", version = "0.1.5" }
either_of = { path = "./either_of/", version = "0.1.6" }
hydration_context = { path = "./hydration_context", version = "0.3.0" }
leptos = { path = "./leptos", version = "0.8.2" }
leptos_config = { path = "./leptos_config", version = "0.8.2" }
leptos_dom = { path = "./leptos_dom", version = "0.8.2" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.8.2" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.8.2" }
leptos_macro = { path = "./leptos_macro", version = "0.8.2" }
leptos_router = { path = "./router", version = "0.8.2" }
leptos_router_macro = { path = "./router_macro", version = "0.8.2" }
leptos_server = { path = "./leptos_server", version = "0.8.2" }
leptos_meta = { path = "./meta", version = "0.8.2" }
leptos = { path = "./leptos", version = "0.8.8" }
leptos_config = { path = "./leptos_config", version = "0.8.7" }
leptos_dom = { path = "./leptos_dom", version = "0.8.6" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.8.5" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.8.5" }
leptos_macro = { path = "./leptos_macro", version = "0.8.8" }
leptos_router = { path = "./router", version = "0.8.6" }
leptos_router_macro = { path = "./router_macro", version = "0.8.5" }
leptos_server = { path = "./leptos_server", version = "0.8.5" }
leptos_meta = { path = "./meta", version = "0.8.5" }
next_tuple = { path = "./next_tuple", version = "0.1.0" }
oco_ref = { path = "./oco", version = "0.2.0" }
oco_ref = { path = "./oco", version = "0.2.1" }
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
reactive_graph = { path = "./reactive_graph", version = "0.2.0" }
reactive_stores = { path = "./reactive_stores", version = "0.2.0" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.2.0" }
server_fn = { path = "./server_fn", version = "0.8.2" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.2" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.8.2" }
tachys = { path = "./tachys", version = "0.2.0" }
reactive_graph = { path = "./reactive_graph", version = "0.2.6" }
reactive_stores = { path = "./reactive_stores", version = "0.2.5" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.2.6" }
server_fn = { path = "./server_fn", version = "0.8.6" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.7" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.8.5" }
tachys = { path = "./tachys", version = "0.2.7" }
wasm_split_helpers = { path = "./wasm_split", version = "0.1.2" }
wasm_split_macros = { path = "./wasm_split_macros", version = "0.1.2" }
# members deps
async-once-cell = { default-features = false, version = "0.5.3" }
itertools = { default-features = false, version = "0.14.0" }
convert_case = { default-features = false, version = "0.8.0" }
serde_json = { default-features = false, version = "1.0.140" }
trybuild = { default-features = false, version = "1.0.105" }
typed-builder = { default-features = false, version = "0.21.0" }
thiserror = { default-features = false, version = "2.0.12" }
serde_json = { default-features = false, version = "1.0.143" }
trybuild = { default-features = false, version = "1.0.110" }
typed-builder = { default-features = false, version = "0.21.2" }
thiserror = { default-features = false, version = "2.0.16" }
wasm-bindgen = { default-features = false, version = "0.2.100" }
indexmap = { default-features = false, version = "2.9.0" }
indexmap = { default-features = false, version = "2.11.0" }
rstml = { default-features = false, version = "0.12.1" }
rustc_version = { default-features = false, version = "0.4.1" }
guardian = { default-features = false, version = "1.3.0" }
@@ -98,17 +100,17 @@ proc-macro-error2 = { default-features = false, version = "2.0.1" }
const_format = { default-features = false, version = "0.2.34" }
gloo-net = { default-features = false, version = "0.6.0" }
url = { default-features = false, version = "2.5.4" }
tokio = { default-features = false, version = "1.45.1" }
tokio = { default-features = false, version = "1.47.1" }
base64 = { default-features = false, version = "0.22.1" }
cfg-if = { default-features = false, version = "1.0.0" }
cfg-if = { default-features = false, version = "1.0.3" }
wasm-bindgen-futures = { default-features = false, version = "0.4.50" }
tower = { default-features = false, version = "0.5.2" }
proc-macro2 = { default-features = false, version = "1.0.95" }
proc-macro2 = { default-features = false, version = "1.0.101" }
serde = { default-features = false, version = "1.0.219" }
parking_lot = { default-features = false, version = "0.12.4" }
axum = { default-features = false, version = "0.8.4" }
serde_qs = { default-features = false, version = "0.15.0" }
syn = { default-features = false, version = "2.0.101" }
syn = { default-features = false, version = "2.0.106" }
xxhash-rust = { default-features = false, version = "0.8.15" }
paste = { default-features = false, version = "1.0.15" }
quote = { default-features = false, version = "1.0.40" }
@@ -116,55 +118,58 @@ web-sys = { default-features = false, version = "0.3.77" }
js-sys = { default-features = false, version = "0.3.77" }
rand = { default-features = false, version = "0.9.1" }
serde-lite = { default-features = false, version = "0.5.0" }
tokio-tungstenite = { default-features = false, version = "0.26.2" }
tokio-tungstenite = { default-features = false, version = "0.27.0" }
serial_test = { default-features = false, version = "3.2.0" }
erased = { default-features = false, version = "0.1.2" }
glib = { default-features = false, version = "0.20.10" }
async-trait = { default-features = false, version = "0.1.88" }
glib = { default-features = false, version = "0.20.12" }
async-trait = { default-features = false, version = "0.1.89" }
typed-builder-macro = { default-features = false, version = "0.21.0" }
linear-map = { default-features = false, version = "1.2.0" }
anyhow = { default-features = false, version = "1.0.98" }
anyhow = { default-features = false, version = "1.0.99" }
walkdir = { default-features = false, version = "2.5.0" }
actix-ws = { default-features = false, version = "0.3.0" }
tower-http = { default-features = false, version = "0.6.4" }
prettyplease = { default-features = false, version = "0.2.33" }
inventory = { default-features = false, version = "0.3.20" }
config = { default-features = false, version = "0.15.11" }
camino = { default-features = false, version = "1.1.9" }
prettyplease = { default-features = false, version = "0.2.37" }
inventory = { default-features = false, version = "0.3.21" }
config = { default-features = false, version = "0.15.14" }
camino = { default-features = false, version = "1.1.11" }
ciborium = { default-features = false, version = "0.2.2" }
multer = { default-features = false, version = "3.1.0" }
leptos-spin-macro = { default-features = false, version = "0.2.0" }
sledgehammer_utils = { default-features = false, version = "0.3.1" }
sledgehammer_bindgen = { default-features = false, version = "0.6.0" }
wasm-streams = { default-features = false, version = "0.4.2" }
rkyv = { default-features = false, version = "0.8.10" }
rkyv = { default-features = false, version = "0.8.11" }
temp-env = { default-features = false, version = "0.3.6" }
uuid = { default-features = false, version = "1.17.0" }
uuid = { default-features = false, version = "1.18.0" }
bytes = { default-features = false, version = "1.10.1" }
http = { default-features = false, version = "1.3.1" }
regex = { default-features = false, version = "1.11.1" }
regex = { default-features = false, version = "1.11.2" }
drain_filter_polyfill = { default-features = false, version = "0.1.3" }
tempfile = { default-features = false, version = "3.20.0" }
futures-lite = { default-features = false, version = "2.6.0" }
tempfile = { default-features = false, version = "3.21.0" }
futures-lite = { default-features = false, version = "2.6.1" }
log = { default-features = false, version = "0.4.27" }
percent-encoding = { default-features = false, version = "2.3.1" }
percent-encoding = { default-features = false, version = "2.3.2" }
async-executor = { default-features = false, version = "1.13.2" }
const-str = { default-features = false, version = "0.6.2" }
const-str = { default-features = false, version = "0.6.4" }
http-body-util = { default-features = false, version = "0.1.3" }
hyper = { default-features = false, version = "1.6.0" }
postcard = { default-features = false, version = "1.1.1" }
hyper = { default-features = false, version = "1.7.0" }
postcard = { default-features = false, version = "1.1.3" }
rmp-serde = { default-features = false, version = "1.3.0" }
reqwest = { default-features = false, version = "0.12.18" }
reqwest = { default-features = false, version = "0.12.23" }
tower-layer = { default-features = false, version = "0.3.3" }
attribute-derive = { default-features = false, version = "0.10.3" }
insta = { default-features = false, version = "1.43.1" }
codee = { default-features = false, version = "0.3.0" }
actix-http = { default-features = false, version = "3.11.0" }
actix-http = { default-features = false, version = "3.11.1" }
wasm-bindgen-test = { default-features = false, version = "0.3.50" }
rustversion = { default-features = false, version = "1.0.21" }
rustversion = { default-features = false, version = "1.0.22" }
getrandom = { default-features = false, version = "0.3.3" }
actix-files = { default-features = false, version = "0.6.6" }
async-lock = { default-features = false, version = "3.4.0" }
async-lock = { default-features = false, version = "3.4.1" }
base16 = { default-features = false, version = "0.2.1" }
digest = { default-features = false, version = "0.10.7" }
sha2 = { default-features = false, version = "0.10.8" }
[profile.release]
codegen-units = 1

View File

@@ -90,35 +90,13 @@ Here are some resources for learning more about Leptos:
- [API Documentation](https://docs.rs/leptos/latest/leptos/)
- [Common Bugs](https://github.com/leptos-rs/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
## `nightly` Note
Most of the examples assume youre using `nightly` version of Rust and the `nightly` feature of Leptos. To use `nightly` Rust, you can either set your toolchain globally or on per-project basis.
To set `nightly` as a default toolchain for all projects (and add the ability to compile Rust to WebAssembly, if you havent already):
```
rustup toolchain install nightly
rustup default nightly
rustup target add wasm32-unknown-unknown
```
If you'd like to use `nightly` only in your Leptos project however, add [`rust-toolchain.toml`](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) file with the following content:
```toml
[toolchain]
channel = "nightly"
targets = ["wasm32-unknown-unknown"]
```
The `nightly` feature enables the function call syntax for accessing and setting signals, as opposed to `.get()` and `.set()`. This leads to a consistent mental model in which accessing a reactive value of any kind (a signal, memo, or derived signal) is always represented as a function call. This is only possible with nightly Rust and the `nightly` feature.
## `cargo-leptos`
[`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our starter templates for [Actix](https://github.com/leptos-rs/start) or [Axum](https://github.com/leptos-rs/start-axum).
```bash
cargo install cargo-leptos
cargo leptos new --git https://github.com/leptos-rs/start
cargo leptos new --git https://github.com/leptos-rs/start-axum
cd [your project name]
cargo leptos watch
```
@@ -147,7 +125,7 @@ Yes, Im sure there are. You can see from the state of our issue tracker over
This may be the big one: “production ready” implies a certain orientation to a library: that you can basically use it, without any special knowledge of its internals or ability to contribute. Everyone has this at some level in their stack: for example I (@gbj) dont have the capacity or knowledge to contribute to something like `wasm-bindgen` at this point: I simply rely on it to work.
There are several people in the community using Leptos right now for internal apps at work, who have also become significant contributors. I think this is the right level of production use for now. There may be missing features that you need, and you may end up building them! But for internal apps, if youre willing to build and contribute missing pieces along the way, the framework is definitely usable right now.
There are several people in the community using Leptos right now for many websites at work, who have also become significant contributors. There may be missing features that you need, and you may end up building them! But, if you're willing to contribute a few missing pieces along the way, the framework is most definitely usable for production applications, especially given the ecosystem of libraries that have sprung up around it.
### Can I use this for native GUI?

View File

@@ -2,8 +2,6 @@
name = "benchmarks"
version = "0.1.0"
edition = "2021"
# std::sync::LazyLock is stabilized in Rust version 1.80.0
rust-version = "1.80.0"
[dependencies]
l0410 = { package = "leptos", version = "0.4.10", features = [

View File

@@ -19,7 +19,7 @@ leptos_meta = { path = "../../meta" }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router" }
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
thiserror = "2.0.12"
tokio = { version = "1.39", features = [
"rt-multi-thread",
"macros",

View File

@@ -0,0 +1,11 @@
extend = [
{ path = "./cargo-leptos.toml" },
{ path = "../cargo-make/webdriver.toml" },
]
[tasks.integration-test]
dependencies = [
"install-cargo-leptos",
"start-webdriver",
"cargo-leptos-e2e-split",
]

View File

@@ -11,6 +11,10 @@ args = ["--locked"]
command = "cargo"
args = ["leptos", "end-to-end"]
[tasks.cargo-leptos-e2e-split]
command = "cargo"
args = ["leptos", "end-to-end", "--split"]
[tasks.build]
clear = true
command = "cargo"

View File

@@ -2,8 +2,6 @@
name = "counter_isomorphic"
version = "0.1.0"
edition = "2021"
# std::sync::LazyLock is stabilized in Rust version 1.80.0
rust-version = "1.80.0"
[lib]
crate-type = ["cdylib", "rlib"]

View File

@@ -18,7 +18,7 @@ tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
tokio = { version = "1.39", features = ["full"], optional = true }
http = { version = "1.1" }
thiserror = "1.0"
thiserror = "2.0.12"
wasm-bindgen = "0.2.93"
[features]

View File

@@ -15,7 +15,7 @@ serde = { version = "1.0", features = ["derive"] }
log = "0.4.22"
console_log = "1.0"
console_error_panic_hook = "0.1.7"
thiserror = "1.0"
thiserror = "2.0.12"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
tracing-subscriber-wasm = "0.1.0"

View File

@@ -4,7 +4,7 @@ mod routes;
use leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet};
use leptos_router::{
components::{FlatRoutes, Route, Router, RoutingProgress},
OptionalParamSegment, ParamSegment, StaticSegment,
Lazy, OptionalParamSegment, ParamSegment, StaticSegment,
};
use routes::{nav::*, stories::*, story::*, users::*};
use std::time::Duration;
@@ -44,8 +44,8 @@ pub fn App() -> impl IntoView {
<Nav />
<main>
<FlatRoutes fallback=|| "Not found.">
<Route path=(StaticSegment("users"), ParamSegment("id")) view=User/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view=Story/>
<Route path=(StaticSegment("users"), ParamSegment("id")) view={Lazy::<UserRoute>::new()}/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view={Lazy::<StoryRoute>::new()}/>
<Route path=OptionalParamSegment("stories") view=Stories/>
</FlatRoutes>
</main>

View File

@@ -1,24 +1,38 @@
use crate::api;
use crate::api::{self, Story};
use leptos::{either::Either, prelude::*};
use leptos_meta::Meta;
use leptos_router::{components::A, hooks::use_params_map};
use leptos_router::{
components::A, hooks::use_params_map, lazy_route, LazyRoute,
};
#[component]
pub fn Story() -> impl IntoView {
let params = use_params_map();
let story = Resource::new_blocking(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {
None
} else {
api::fetch_api::<api::Story>(&api::story(&format!("item/{id}")))
#[derive(Debug)]
pub struct StoryRoute {
story: Resource<Option<Story>>,
}
#[lazy_route]
impl LazyRoute for StoryRoute {
fn data() -> Self {
let params = use_params_map();
let story = Resource::new_blocking(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {
None
} else {
api::fetch_api::<api::Story>(&api::story(&format!(
"item/{id}"
)))
.await
}
},
);
}
},
);
Self { story }
}
Suspense(SuspenseProps::builder().fallback(|| "Loading...").children(ToChildren::to_children(move || Suspend::new(async move {
fn view(this: Self) -> AnyView {
let StoryRoute { story } = this;
Suspense(SuspenseProps::builder().fallback(|| "Loading...").children(ToChildren::to_children(move || Suspend::new(async move {
match story.await.clone() {
None => Either::Left("Story not found."),
Some(story) => {
@@ -61,7 +75,8 @@ pub fn Story() -> impl IntoView {
})
}
}
}))).build())
}))).build()).into_any()
}
}
#[component]

View File

@@ -1,46 +1,58 @@
use crate::api::{self, User};
use leptos::{either::Either, prelude::*, server::Resource};
use leptos_router::hooks::use_params_map;
use leptos_router::{hooks::use_params_map, lazy_route, LazyRoute};
#[component]
pub fn User() -> impl IntoView {
let params = use_params_map();
let user = Resource::new(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {
None
} else {
api::fetch_api::<User>(&api::user(&id)).await
}
},
);
view! {
<div class="user-view">
<Suspense fallback=|| view! { "Loading..." }>
{move || Suspend::new(async move { match user.await.clone() {
None => Either::Left(view! { <h1>"User not found."</h1> }),
Some(user) => Either::Right(view! {
<div>
<h1>"User: " {user.id.clone()}</h1>
<ul class="meta">
<li>
<span class="label">"Created: "</span> {user.created}
</li>
<li>
<span class="label">"Karma: "</span> {user.karma}
</li>
<li inner_html={user.about} class="about"></li>
</ul>
<p class="links">
<a href=format!("https://news.ycombinator.com/submitted?id={}", user.id)>"submissions"</a>
" | "
<a href=format!("https://news.ycombinator.com/threads?id={}", user.id)>"comments"</a>
</p>
</div>
})
}})}
</Suspense>
</div>
#[derive(Debug)]
pub struct UserRoute {
user: Resource<Option<User>>,
}
#[lazy_route]
impl LazyRoute for UserRoute {
fn data() -> Self {
let params = use_params_map();
let user = Resource::new(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {
None
} else {
api::fetch_api::<User>(&api::user(&id)).await
}
},
);
UserRoute { user }
}
fn view(this: Self) -> AnyView {
let UserRoute { user } = this;
view! {
<div class="user-view">
<Suspense fallback=|| view! { "Loading..." }>
{move || Suspend::new(async move { match user.await.clone() {
None => Either::Left(view! { <h1>"User not found."</h1> }),
Some(user) => Either::Right(view! {
<div>
<h1>"User: " {user.id.clone()}</h1>
<ul class="meta">
<li>
<span class="label">"Created: "</span> {user.created}
</li>
<li>
<span class="label">"Karma: "</span> {user.karma}
</li>
<li inner_html={user.about} class="about"></li>
</ul>
<p class="links">
<a href=format!("https://news.ycombinator.com/submitted?id={}", user.id)>"submissions"</a>
" | "
<a href=format!("https://news.ycombinator.com/threads?id={}", user.id)>"comments"</a>
</p>
</div>
})
}})}
</Suspense>
</div>
}.into_any()
}
}

View File

@@ -0,0 +1,95 @@
[package]
name = "lazy_routes"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
axum = { version = "0.8.1", optional = true }
console_error_panic_hook = "0.1.7"
console_log = "1.0"
leptos = { path = "../../leptos", features = ["tracing"] }
leptos_meta = { path = "../../meta" }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router" }
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
tokio = { version = "1.39", features = [
"rt-multi-thread",
"macros",
"time",
], optional = true }
wasm-bindgen = "0.2.92"
futures = "0.3.31"
serde_json = "1.0.140"
gloo-timers = { version = "0.3", features = ["futures"] }
[features]
hydrate = ["leptos/hydrate"]
ssr = [
"dep:axum",
"dep:tokio",
"leptos/ssr",
"leptos_meta/ssr",
"dep:leptos_axum",
"leptos_router/ssr",
]
[profile.release]
panic = "abort"
[profile.wasm-release]
inherits = "release"
opt-level = 'z'
lto = true
codegen-units = 1
panic = "abort"
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "sqlx", "leptos_axum"]
skip_feature_sets = [["ssr", "hydrate"]]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name = "regression"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
site-root = "target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
# Defaults to pkg
site-pkg-dir = "pkg"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
# [Windows] for non-WSL use "npx.cmd playwright test"
# This binary name can be checked in Powershell with Get-Command npx
end2end-cmd = "cargo make test-ui"
end2end-dir = "e2e"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"
# The features to use when compiling the bin target
#
# Optional. Can be over-ridden with the command line parameter --bin-features
bin-features = ["ssr"]
# If the --no-default-features flag should be used when compiling the bin target
#
# Optional. Defaults to false.
bin-default-features = false
# The features to use when compiling the lib target
#
# Optional. Can be over-ridden with the command line parameter --lib-features
lib-features = ["hydrate"]
# If the --no-default-features flag should be used when compiling the lib target
#
# Optional. Defaults to false.
lib-default-features = false

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Leptos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,8 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/cargo-leptos-split-webdriver-test.toml" },
]
[env]
CLIENT_PROCESS_NAME = "regression"

View File

@@ -0,0 +1,8 @@
# Regression Tests
This example functions as a catch-all for all current and future regression
test cases that typically happens at integration.
## Quick Start
Run `cargo leptos watch` to run this example.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,18 @@
[package]
name = "lazy_routes_e2e"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
anyhow = "1.0"
async-trait = "0.1.81"
cucumber = "0.21.1"
fantoccini = "0.21.1"
pretty_assertions = "1.4"
serde_json = "1.0"
tokio = { version = "1.39", features = ["macros", "rt-multi-thread", "time"] }
url = "2.5"
[[test]]
name = "app_suite"
harness = false # Allow Cucumber to print output instead of libtest

View File

@@ -0,0 +1,20 @@
extend = { path = "../../cargo-make/main.toml" }
[tasks.test]
env = { RUN_AUTOMATICALLY = false }
condition = { env_true = ["RUN_AUTOMATICALLY"] }
[tasks.ci]
[tasks.test-ui]
command = "cargo"
args = [
"test",
"--test",
"app_suite",
"--",
"--retry",
"2",
"--fail-fast",
"${@}",
]

View File

@@ -0,0 +1,30 @@
# Lazy Routes
This example demonstrates how to split the WASM bundle that is sent to the client into multiple binaries, which can be lazy-loaded, either independently or in a way that's integrated into the router.
Without code splitting, the entire application is compiled to a monolithic WASM binary, the size of which grows in proportion to the complexity of the application. This means that the time to interactive (TTI) for any page is proportional to the size of the entire application, not only that page.
Code splitting allows you to lazy-load some functions, by splitting off the WASM binary code for certain functions into separate files, which can be downloaded as needed. This minimizes initial TTI for any page, and then amortizes the cost of loading the binary over the lifetime of the application session.
In many cases, this can be done with minimal or no cost.
Lazy loading can be used in two ways, each of which is shown in the example.
## `#[lazy]` macro
`#[lazy]` is an attribute macro that can be used to annotate an `async fn` in order to split its code out into a separate file that will be loaded on demand, when compiled with `cargo leptos --split`.
This has some limitations (for example, it must return concrete types) but can be used for most functions.
## `LazyRoute`
`LazyRoute` is a specialized application of `#[lazy]` that allows you to define an entire route/page of your application as being lazy-loaded.
Creating a lazy route requires you to split the route into two parts:
1. `data()`: A synchronous method that should be used to start loading any async data used by the page, for example by creating a `Resource`
2. `view()`: An async (because lazy-loaded) method that renders the view.
The purpose of splitting these into two parts is to avoid a “waterfall,” in which the browser first waits for a lazy-loaded WASM chunk that defines the page, _then_ makes a second request to the server to load the relevant data. Instead, a `LazyRoute` will begin loading resources created in the `data` method while lazy-loading the component body in the `view`, then render the route.
This means that in many cases, the data loading “hides” the cost of the lazy-loading; i.e., the page needs to wait for the data to load, so the fact that it is waiting concurrently for the lazy-loaded view means that the lazy loading does not cost anything additional in terms of page load time.

View File

@@ -0,0 +1,33 @@
@basic
Feature: Check that each page hydrates correctly
Scenario: Page A is rendered correctly.
Given I see the app
Then I see the page is View A
Scenario: Page A hydrates and allows navigating to page B.
Given I see the app
When I select the link B
Then I see the navigating indicator
When I wait for a second
Then I see the page is View B
Scenario: Page B is rendered correctly.
When I open the app at /b
Then I see the page is View B
Scenario: Page B hydrates and allows navigating to page C.
When I open the app at /b
When I select the link C
Then I see the navigating indicator
When I wait for a second
Then I see the page is View C
Scenario: Page C is rendered correctly.
When I open the app at /c
Then I see the page is View C
Scenario: Page C hydrates and allows navigating to page A.
When I open the app at /c
When I select the link A
Then I see the page is View A

View File

@@ -0,0 +1,15 @@
@duplicate_names
Feature: Lazy functions can share the same name
Scenario: Two functions with the same name both work.
Given I see the app
Then I see the page is View A
When I click the button First
When I wait for a second
Then I see the result is {"a":"First Value","b":1}
When I click the button Second
When I wait for a second
Then I see the result is {"a":"Second Value","b":2}
When I click the button Third
When I wait for a second
Then I see the result is Third value.

View File

@@ -0,0 +1,9 @@
@shared_chunks
Feature: Shared code splitting works correctly
Scenario: Two functions using same serde code both work.
Given I see the app
Then I see the page is View A
When I click the button First
When I wait for a second
Then I see the result is {"a":"First Value","b":1}

View File

@@ -0,0 +1,30 @@
mod fixtures;
use anyhow::Result;
use cucumber::World;
use fixtures::world::AppWorld;
use std::{ffi::OsStr, fs::read_dir};
#[tokio::main]
async fn main() -> Result<()> {
// Normally the below is done, but it's now gotten to the point of
// having a sufficient number of tests where the resource contention
// of the concurrently running browsers will cause failures on CI.
// AppWorld::cucumber()
// .fail_on_skipped()
// .run_and_exit("./features")
// .await;
// Mitigate the issue by manually stepping through each feature,
// rather than letting cucumber glob them and dispatch all at once.
for entry in read_dir("./features")? {
let path = entry?.path();
if path.extension() == Some(OsStr::new("feature")) {
AppWorld::cucumber()
.fail_on_skipped()
.run_and_exit(path)
.await;
}
}
Ok(())
}

View File

@@ -0,0 +1,23 @@
use super::{find, world::HOST};
use anyhow::Result;
use fantoccini::Client;
use std::result::Result::Ok;
pub async fn goto_path(client: &Client, path: &str) -> Result<()> {
let url = format!("{}{}", HOST, path);
client.goto(&url).await?;
Ok(())
}
pub async fn click_link(client: &Client, text: &str) -> Result<()> {
let link = find::link_with_text(&client, &text).await?;
link.click().await?;
Ok(())
}
pub async fn click_button(client: &Client, id: &str) -> Result<()> {
let btn = find::element_by_id(&client, &id).await?;
btn.click().await?;
Ok(())
}

View File

@@ -0,0 +1,29 @@
use crate::fixtures::find;
use anyhow::{Ok, Result};
use fantoccini::Client;
use pretty_assertions::assert_eq;
pub async fn page_name_is(client: &Client, expected_text: &str) -> Result<()> {
let actual = find::text_at_id(client, "page").await?;
assert_eq!(&actual, expected_text);
Ok(())
}
pub async fn result_is(client: &Client, expected_text: &str) -> Result<()> {
let actual = find::text_at_id(client, "result").await?;
assert_eq!(&actual, expected_text);
Ok(())
}
pub async fn navigating_appears(client: &Client) -> Result<()> {
let actual = find::text_at_id(client, "navigating").await?;
assert_eq!(&actual, "Navigating...");
Ok(())
}
pub async fn element_exists(client: &Client, id: &str) -> Result<()> {
find::element_by_id(client, id)
.await
.expect(&format!("could not find element with id `{id}`"));
Ok(())
}

View File

@@ -0,0 +1,23 @@
use anyhow::{Ok, Result};
use fantoccini::{elements::Element, Client, Locator};
pub async fn text_at_id(client: &Client, id: &str) -> Result<String> {
let element = element_by_id(client, id)
.await
.expect(format!("no such element with id `{}`", id).as_str());
let text = element.text().await?;
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)
}
pub async fn element_by_id(client: &Client, id: &str) -> Result<Element> {
Ok(client.wait().for_element(Locator::Id(id)).await?)
}

View File

@@ -0,0 +1,4 @@
pub mod action;
pub mod check;
pub mod find;
pub mod world;

View File

@@ -0,0 +1,68 @@
use crate::fixtures::{action, world::AppWorld};
use anyhow::{Ok, Result};
use cucumber::{gherkin::Step, given, when};
#[given("I see the app")]
#[when("I open the app")]
async fn i_open_the_app(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
action::goto_path(client, "").await?;
Ok(())
}
#[when(regex = "^I open the app at (.*)$")]
async fn i_open_the_app_at(world: &mut AppWorld, url: String) -> Result<()> {
let client = &world.client;
action::goto_path(client, &url).await?;
Ok(())
}
#[when(regex = "^I select the link (.*)$")]
async fn i_select_the_link(world: &mut AppWorld, text: String) -> Result<()> {
let client = &world.client;
action::click_link(client, &text).await?;
Ok(())
}
#[when(regex = "^I click the button (.*)$")]
async fn i_click_the_button(world: &mut AppWorld, id: String) -> Result<()> {
let client = &world.client;
action::click_button(client, &id).await?;
Ok(())
}
#[when(expr = "I select the following links")]
async fn i_select_the_following_links(
world: &mut AppWorld,
step: &Step,
) -> Result<()> {
let client = &world.client;
if let Some(table) = step.table.as_ref() {
for row in table.rows.iter() {
action::click_link(client, &row[0]).await?;
}
}
Ok(())
}
#[when("I wait for a second")]
async fn i_wait_for_a_second(world: &mut AppWorld) -> Result<()> {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
Ok(())
}
#[given(regex = "^I (refresh|reload) the (browser|page)$")]
#[when(regex = "^I (refresh|reload) the (browser|page)$")]
async fn i_refresh_the_browser(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
client.refresh().await?;
Ok(())
}

View File

@@ -0,0 +1,31 @@
use crate::fixtures::{check, world::AppWorld};
use anyhow::{Ok, Result};
use cucumber::then;
#[then(regex = r"^I see the navigating indicator")]
async fn i_see_the_nav(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
check::navigating_appears(client).await?;
Ok(())
}
#[then(regex = r"^I see the page is (.*)$")]
async fn i_see_the_page_is(world: &mut AppWorld, text: String) -> Result<()> {
let client = &world.client;
check::page_name_is(client, &text).await?;
Ok(())
}
#[then(regex = r"^I see the result is (.*)$")]
async fn i_see_the_result_is(world: &mut AppWorld, text: String) -> Result<()> {
let client = &world.client;
check::result_is(client, &text).await?;
Ok(())
}
#[then(regex = r"^I see the navbar$")]
async fn i_see_the_navbar(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
check::element_exists(client, "nav").await?;
Ok(())
}

View File

@@ -0,0 +1,39 @@
pub mod action_steps;
pub mod check_steps;
use anyhow::Result;
use cucumber::World;
use fantoccini::{
error::NewSessionError, wd::Capabilities, Client, ClientBuilder,
};
pub const HOST: &str = "http://127.0.0.1:3000";
#[derive(Debug, World)]
#[world(init = Self::new)]
pub struct AppWorld {
pub client: Client,
}
impl AppWorld {
async fn new() -> Result<Self, anyhow::Error> {
let webdriver_client = build_client().await?;
Ok(Self {
client: webdriver_client,
})
}
}
async fn build_client() -> Result<Client, NewSessionError> {
let mut cap = Capabilities::new();
let arg = serde_json::from_str("{\"args\": [\"-headless\"]}").unwrap();
cap.insert("goog:chromeOptions".to_string(), arg);
let client = ClientBuilder::native()
.capabilities(cap)
.connect("http://localhost:4444")
.await?;
Ok(client)
}

View File

@@ -0,0 +1,355 @@
use leptos::{prelude::*, task::spawn_local};
use leptos_router::{
components::{Outlet, ParentRoute, Route, Router, Routes},
lazy_route, Lazy, LazyRoute, StaticSegment,
};
use serde::{Deserialize, Serialize};
pub fn shell(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=options.clone()/>
<HydrationScripts options/>
</head>
<body>
<App/>
</body>
</html>
}
}
#[component]
pub fn App() -> impl IntoView {
let count = RwSignal::new(0);
provide_context(count);
let (is_routing, set_is_routing) = signal(false);
view! {
<nav id="nav" style="width: 100%">
<a href="/">"A"</a> " | "
<a href="/b">"B"</a> " | "
<a href="/c">"C"</a> " | "
<a href="/d">"D"</a>
<span style="float: right" id="navigating">
{move || is_routing.get().then_some("Navigating...")}
</span>
</nav>
<Router set_is_routing>
<Routes fallback=|| "Not found.">
<Route path=StaticSegment("") view=ViewA/>
<Route path=StaticSegment("b") view=ViewB/>
<Route path=StaticSegment("c") view={Lazy::<ViewC>::new()}/>
// you can nest lazy routes, and there data and views will all load concurrently
<ParentRoute path=StaticSegment("d") view={Lazy::<ViewD>::new()}>
<Route path=StaticSegment("") view={Lazy::<ViewE>::new()}/>
</ParentRoute>
</Routes>
</Router>
}
}
// View A: A plain old synchronous route, just like they all currently work. The WASM binary code
// for this is shipped as part of the main bundle. Any data-loading code (like resources that run
// in the body of the component) will be shipped as part of the main bundle.
#[component]
pub fn ViewA() -> impl IntoView {
leptos::logging::log!("View A");
let result = RwSignal::new("Click a button to see the result".to_string());
view! {
<p id="page">"View A"</p>
<pre id="result">{result}</pre>
<button id="First" on:click=move |_| spawn_local(async move { result.set(first_value().await); })>"First"</button>
<button id="Second" on:click=move |_| spawn_local(async move { result.set(second_value().await); })>"Second"</button>
// test to make sure duplicate names in different scopes can be used
<button id="Third" on:click=move |_| {
#[lazy]
pub fn second_value() -> String {
"Third value.".to_string()
}
spawn_local(async move {
result.set(second_value().await);
});
}>"Third"</button>
}
}
// View B: lazy-loaded route with lazy-loaded data
#[derive(Debug, Clone, Deserialize)]
pub struct Comment {
#[serde(rename = "postId")]
post_id: usize,
id: usize,
name: String,
email: String,
body: String,
}
#[lazy]
fn deserialize_comments(data: &str) -> Vec<Comment> {
serde_json::from_str(data).unwrap()
}
#[component]
pub fn ViewB() -> impl IntoView {
let data = LocalResource::new(|| async move {
let preload = deserialize_comments("[]");
let (_, data) = futures::future::join(preload, async {
gloo_timers::future::TimeoutFuture::new(500).await;
r#"
[
{
"postId": 1,
"id": 1,
"name": "id labore ex et quam laborum",
"email": "Eliseo@gardner.biz",
"body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium"
},
{
"postId": 1,
"id": 2,
"name": "quo vero reiciendis velit similique earum",
"email": "Jayne_Kuhic@sydney.com",
"body": "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et"
},
{
"postId": 1,
"id": 3,
"name": "odio adipisci rerum aut animi",
"email": "Nikita@garfield.biz",
"body": "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione"
}
]
"#
})
.await;
deserialize_comments(data).await
});
view! {
<p id="page">"View B"</p>
<Suspense fallback=|| view! { <p id="loading">"Loading..."</p> }>
<ul>
{move || Suspend::new(async move {
let items = data.await;
items.into_iter()
.map(|comment| view! {
<li id=format!("{}-{}", comment.post_id, comment.id)>
<strong>{comment.name}</strong> " (by " {comment.email} ")"<br/>
{comment.body}
</li>
})
.collect_view()
})}
</ul>
</Suspense>
}
.into_any()
}
#[derive(Debug, Clone, Deserialize)]
pub struct Album {
#[serde(rename = "userId")]
user_id: usize,
id: usize,
title: String,
}
// View C: a lazy view, and some data, loaded in parallel when we navigate to /c.
#[derive(Clone)]
pub struct ViewC {
data: LocalResource<Vec<Album>>,
}
// Lazy-loaded routes need to implement the LazyRoute trait. They define a "route data" struct,
// which is created with `::data()`, and then a separate view function which is lazily loaded.
//
// This is important because it allows us to concurrently 1) load the route data, and 2) lazily
// load the component, rather than creating a "waterfall" where we can't start loading the route
// data until we've received the view.
//
// The `#[lazy_route]` macro makes `view` into a lazy-loaded inner function, replacing `self` with
// `this`.
#[lazy_route]
impl LazyRoute for ViewC {
fn data() -> Self {
// the data method itself is synchronous: it typically creates things like Resources,
// which are created synchronously but spawn an async data-loading task
// if you want further code-splitting, however, you can create a lazy function to load the data!
#[lazy]
async fn lazy_data() -> Vec<Album> {
gloo_timers::future::TimeoutFuture::new(250).await;
vec![
Album {
user_id: 1,
id: 1,
title: "quidem molestiae enim".into(),
},
Album {
user_id: 1,
id: 2,
title: "sunt qui excepturi placeat culpa".into(),
},
Album {
user_id: 1,
id: 3,
title: "omnis laborum odio".into(),
},
]
}
Self {
data: LocalResource::new(lazy_data),
}
}
fn view(this: Self) -> AnyView {
let albums = move || {
Suspend::new(async move {
this.data
.await
.into_iter()
.map(|album| {
view! {
<li id=format!("{}-{}", album.user_id, album.id)>
{album.title}
</li>
}
})
.collect::<Vec<_>>()
})
};
view! {
<p id="page">"View C"</p>
<hr/>
<Suspense fallback=|| view! { <p id="loading">"Loading..."</p> }>
<ul>{albums}</ul>
</Suspense>
}
.into_any()
}
}
// When two functions have shared code, that shared code will be split out automatically
// into an additional file. For example, the shared serde code here will be split into a single file,
// and then loaded lazily once when the first of the two functions is called
#[lazy]
pub fn first_value() -> String {
#[derive(Serialize)]
struct FirstValue {
a: String,
b: i32,
}
serde_json::to_string(&FirstValue {
a: "First Value".into(),
b: 1,
})
.unwrap()
}
#[lazy]
pub fn second_value() -> String {
#[derive(Serialize)]
struct SecondValue {
a: String,
b: i32,
}
serde_json::to_string(&SecondValue {
a: "Second Value".into(),
b: 2,
})
.unwrap()
}
struct ViewD {
data: Resource<Result<Vec<i32>, ServerFnError>>,
}
#[lazy_route]
impl LazyRoute for ViewD {
fn data() -> Self {
Self {
data: Resource::new(|| (), |_| d_data()),
}
}
fn view(this: Self) -> AnyView {
let items = move || {
Suspend::new(async move {
this.data
.await
.unwrap_or_default()
.into_iter()
.map(|item| view! { <li>{item}</li> })
.collect::<Vec<_>>()
})
};
view! {
<p id="page">"View D"</p>
<hr/>
<Suspense fallback=|| view! { <p id="loading">"Loading..."</p> }>
<ul>{items}</ul>
</Suspense>
<Outlet/>
}
.into_any()
}
}
// Server functions can be made lazy by combining the two macros,
// with `#[server]` coming first, then `#[lazy]`
#[server]
#[lazy]
async fn d_data() -> Result<Vec<i32>, ServerFnError> {
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
Ok(vec![1, 1, 2, 3, 5, 8, 13])
}
struct ViewE {
data: Resource<Result<Vec<String>, ServerFnError>>,
}
#[lazy_route]
impl LazyRoute for ViewE {
fn data() -> Self {
Self {
data: Resource::new(|| (), |_| e_data()),
}
}
fn view(this: Self) -> AnyView {
let items = move || {
Suspend::new(async move {
this.data
.await
.unwrap_or_default()
.into_iter()
.map(|item| view! { <li>{item}</li> })
.collect::<Vec<_>>()
})
};
view! {
<p id="page">"View E"</p>
<hr/>
<Suspense fallback=|| view! { <p id="loading">"Loading..."</p> }>
<ul>{items}</ul>
</Suspense>
}
.into_any()
}
}
#[server]
async fn e_data() -> Result<Vec<String>, ServerFnError> {
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
Ok(vec!["foo".into(), "bar".into(), "baz".into()])
}

View File

@@ -0,0 +1,9 @@
pub mod app;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use app::*;
console_error_panic_hook::set_once();
leptos::mount::hydrate_lazy(App);
}

View File

@@ -0,0 +1,37 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::Router;
use lazy_routes::app::{shell, App};
use leptos::prelude::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
let conf = get_configuration(None).unwrap();
let addr = conf.leptos_options.site_addr;
let leptos_options = conf.leptos_options;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(App);
let app = Router::new()
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())
})
.fallback(leptos_axum::file_and_error_handler(shell))
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
println!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
#[cfg(not(feature = "ssr"))]
pub fn main() {
// no client-side main function
// unless we want this to work with e.g., Trunk for pure client-side testing
// see lib.rs for hydration function instead
}

View File

@@ -0,0 +1,3 @@
body {
font-family: sans-serif;
}

View File

@@ -0,0 +1,90 @@
[package]
name = "regression"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
axum = { version = "0.8.1", optional = true }
console_error_panic_hook = "0.1.7"
console_log = "1.0"
leptos = { path = "../../leptos", features = ["tracing"] }
leptos_meta = { path = "../../meta" }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router" }
serde = { version = "1.0", features = ["derive"] }
thiserror = "2.0.12"
tokio = { version = "1.39", features = [ "rt-multi-thread", "macros", "time" ], optional = true }
wasm-bindgen = "0.2.92"
[features]
hydrate = [
"leptos/hydrate",
]
ssr = [
"dep:axum",
"dep:tokio",
"leptos/ssr",
"leptos_meta/ssr",
"dep:leptos_axum",
"leptos_router/ssr",
]
[profile.release]
panic = "abort"
[profile.wasm-release]
inherits = "release"
opt-level = 'z'
lto = true
codegen-units = 1
panic = "abort"
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "sqlx", "leptos_axum"]
skip_feature_sets = [["ssr", "hydrate"]]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name = "regression"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
site-root = "target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
# Defaults to pkg
site-pkg-dir = "pkg"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
# [Windows] for non-WSL use "npx.cmd playwright test"
# This binary name can be checked in Powershell with Get-Command npx
end2end-cmd = "cargo make test-ui"
end2end-dir = "e2e"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"
# The features to use when compiling the bin target
#
# Optional. Can be over-ridden with the command line parameter --bin-features
bin-features = ["ssr"]
# If the --no-default-features flag should be used when compiling the bin target
#
# Optional. Defaults to false.
bin-default-features = false
# The features to use when compiling the lib target
#
# Optional. Can be over-ridden with the command line parameter --lib-features
lib-features = ["hydrate"]
# If the --no-default-features flag should be used when compiling the lib target
#
# Optional. Defaults to false.
lib-default-features = false

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Leptos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

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

View File

@@ -0,0 +1,8 @@
# Regression Tests
This example functions as a catch-all for all current and future regression
test cases that typically happens at integration.
## Quick Start
Run `cargo leptos watch` to run this example.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,18 @@
[package]
name = "regression_e2e"
version = "0.1.0"
edition = "2021"
[dev-dependencies]
anyhow = "1.0"
async-trait = "0.1.81"
cucumber = "0.21.1"
fantoccini = "0.21.1"
pretty_assertions = "1.4"
serde_json = "1.0"
tokio = { version = "1.39", features = ["macros", "rt-multi-thread", "time"] }
url = "2.5"
[[test]]
name = "app_suite"
harness = false # Allow Cucumber to print output instead of libtest

View File

@@ -0,0 +1,20 @@
extend = { path = "../../cargo-make/main.toml" }
[tasks.test]
env = { RUN_AUTOMATICALLY = false }
condition = { env_true = ["RUN_AUTOMATICALLY"] }
[tasks.ci]
[tasks.test-ui]
command = "cargo"
args = [
"test",
"--test",
"app_suite",
"--",
"--retry",
"2",
"--fail-fast",
"${@}",
]

View File

@@ -0,0 +1,34 @@
# E2E Testing
This example demonstrates e2e testing with Rust using executable requirements.
## Testing Stack
| | Role | Description |
|---|---|---|
| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |
| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |
| [Cargo Leptos](https://github.com/leptos-rs/cargo-leptos) | Build Tool | Compile example and start the server and end-2-end tests |
| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome |
## Testing Organization
Testing is organized around what a user can do and see/not see. Test scenarios are grouped by the **user action** and the **object** of that action. This makes it easier to locate and reason about requirements.
Here is a brief overview of how things fit together.
```bash
features
└── {action}_{object}.feature # Specify test scenarios
tests
├── fixtures
│ ├── action.rs # Perform a user action (click, type, etc.)
│ ├── check.rs # Assert what a user can see/not see
│ ├── find.rs # Query page elements
│ ├── mod.rs
│ └── world
│ ├── action_steps.rs # Map Gherkin steps to user actions
│ ├── check_steps.rs # Map Gherkin steps to user expectations
│ └── mod.rs
└── app_suite.rs # Test main
```

View File

@@ -0,0 +1,7 @@
@check_issue_4005
Feature: Check that issue 4005 does not reappear
Scenario: The second item is selected.
Given I see the app
And I can access regression test 4005
Then I see the value of select is 2

View File

@@ -0,0 +1,20 @@
@check_issue_4088
Feature: Check that issue 4088 does not reappear
Scenario: I can see the navbar
Given I see the app
And I can access regression test 4088
Then I see the navbar
Scenario: The user info is shared via context
Given I see the app
And I can access regression test 4088
When I select the link Class 1
Then I see the result is the string Assignments for team of user with id 42
Scenario: The user info is shared via context
Given I see the app
And I can access regression test 4088
When I select the link Class 1
When I refresh the browser
Then I see the result is the string Assignments for team of user with id 42

View File

@@ -0,0 +1,9 @@
@check_issue_4217
Feature: Check that issue 4217 does not reappear
Scenario: All items are selected.
Given I see the app
And I can access regression test 4217
Then I see option1 is selected
And I see option2 is selected
And I see option3 is selected

View File

@@ -0,0 +1,9 @@
@check_issue_4285
Feature: Check that issue 4285 does not reappear
Scenario: Navigating several times to same lazy route does not cause issues.
Given I see the app
And I can access regression test 4285
And I can access regression test 4285
And I can access regression test 4285
Then I see the result is the string 42

View File

@@ -0,0 +1,18 @@
@check_issue_4296
Feature: Check that issue 4296 does not reappear
Scenario: Query param signals created in LazyRoute::data() are reactive in ::view().
Given I see the app
And I can access regression test 4296
Then I see the result is the string None
When I select the link abc
Then I see the result is the string Some("abc")
When I select the link def
Then I see the result is the string Some("def")
Scenario: Loading page with query signal works as well.
Given I see the app
And I can access regression test 4296
When I select the link abc
When I reload the page
Then I see the result is the string Some("abc")

View File

@@ -0,0 +1,8 @@
@check_pr_4015
Feature: Check that PR 4015 does not regress
Scenario: The correct text appears
Given I see the app
And I can access regression test 4015
Then I see the result is the string Some(42)

View File

@@ -0,0 +1,48 @@
@check_pr_4091
Feature: Regression from pull request 4091
Scenario: Signal for testing should work
Given I see the app
And I can access regression test 4091
When I select the link test1
Then I see the result is the string Test1
Scenario: The result returns to empty due to on_cleanup
Given I see the app
And I can access regression test 4091
When I select the following links
| test1 |
| 4091 Home |
Then I see the result is empty
Scenario: The result does not accumulate due to on_cleanup
Given I see the app
And I can access regression test 4091
When I select the following links
| test1 |
| 4091 Home |
| test1 |
| 4091 Home |
Then I see the result is empty
Scenario: I can see the navbar
Given I see the app
And I can access regression test 4091
Then I see the navbar
Scenario: If I navigate to home and back, I can still see the navbar
Given I see the app
And I can access regression test 4091
When I select the following links
| Home |
| 4091 |
Then I see the navbar
Scenario: The signal is not disposed too early
Given I see the app
And I can access regression test 4091
When I select the following links
| test1 |
| Home |
| 4091 |
Then I see the navbar

View File

@@ -0,0 +1,30 @@
mod fixtures;
use anyhow::Result;
use cucumber::World;
use fixtures::world::AppWorld;
use std::{ffi::OsStr, fs::read_dir};
#[tokio::main]
async fn main() -> Result<()> {
// Normally the below is done, but it's now gotten to the point of
// having a sufficient number of tests where the resource contention
// of the concurrently running browsers will cause failures on CI.
// AppWorld::cucumber()
// .fail_on_skipped()
// .run_and_exit("./features")
// .await;
// Mitigate the issue by manually stepping through each feature,
// rather than letting cucumber glob them and dispatch all at once.
for entry in read_dir("./features")? {
let path = entry?.path();
if path.extension() == Some(OsStr::new("feature")) {
AppWorld::cucumber()
.fail_on_skipped()
.run_and_exit(path)
.await;
}
}
Ok(())
}

View File

@@ -0,0 +1,17 @@
use super::{find, world::HOST};
use anyhow::Result;
use fantoccini::Client;
use std::result::Result::Ok;
pub async fn goto_path(client: &Client, path: &str) -> Result<()> {
let url = format!("{}{}", HOST, path);
client.goto(&url).await?;
Ok(())
}
pub async fn click_link(client: &Client, text: &str) -> Result<()> {
let link = find::link_with_text(&client, &text).await?;
link.click().await?;
Ok(())
}

View File

@@ -0,0 +1,45 @@
use crate::fixtures::find;
use anyhow::{Ok, Result};
use fantoccini::Client;
use pretty_assertions::assert_eq;
pub async fn result_text_is(
client: &Client,
expected_text: &str,
) -> Result<()> {
let actual = find::text_at_id(client, "result").await?;
assert_eq!(&actual, expected_text);
Ok(())
}
pub async fn element_exists(client: &Client, id: &str) -> Result<()> {
find::element_by_id(client, id)
.await
.expect(&format!("could not find element with id `{id}`"));
Ok(())
}
pub async fn select_option_is_selected(
client: &Client,
id: &str,
) -> Result<()> {
let el = find::element_by_id(client, id)
.await
.expect(&format!("could not find element with id `{id}`"));
let selected = el.prop("selected").await?;
assert_eq!(selected.as_deref(), Some("true"));
Ok(())
}
pub async fn element_value_is(
client: &Client,
id: &str,
expected: &str,
) -> Result<()> {
let el = find::element_by_id(client, id)
.await
.expect(&format!("could not find element with id `{id}`"));
let value = el.prop("value").await?;
assert_eq!(value.as_deref(), Some(expected));
Ok(())
}

View File

@@ -0,0 +1,23 @@
use anyhow::{Ok, Result};
use fantoccini::{elements::Element, Client, Locator};
pub async fn text_at_id(client: &Client, id: &str) -> Result<String> {
let element = element_by_id(client, id)
.await
.expect(format!("no such element with id `{}`", id).as_str());
let text = element.text().await?;
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)
}
pub async fn element_by_id(client: &Client, id: &str) -> Result<Element> {
Ok(client.wait().for_element(Locator::Id(id)).await?)
}

View File

@@ -0,0 +1,4 @@
pub mod action;
pub mod check;
pub mod find;
pub mod world;

View File

@@ -0,0 +1,47 @@
use crate::fixtures::{action, world::AppWorld};
use anyhow::{Ok, Result};
use cucumber::{gherkin::Step, given, when};
#[given("I see the app")]
#[when("I open the app")]
async fn i_open_the_app(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
action::goto_path(client, "").await?;
Ok(())
}
#[given(regex = "^I can access regression test (.*)$")]
#[when(regex = "^I select the link (.*)$")]
async fn i_select_the_link(world: &mut AppWorld, text: String) -> Result<()> {
let client = &world.client;
action::click_link(client, &text).await?;
Ok(())
}
#[given(expr = "I select the following links")]
#[when(expr = "I select the following links")]
async fn i_select_the_following_links(
world: &mut AppWorld,
step: &Step,
) -> Result<()> {
let client = &world.client;
if let Some(table) = step.table.as_ref() {
for row in table.rows.iter() {
action::click_link(client, &row[0]).await?;
}
}
Ok(())
}
#[given(regex = "^I (refresh|reload) the (browser|page)$")]
#[when(regex = "^I (refresh|reload) the (browser|page)$")]
async fn i_refresh_the_browser(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
client.refresh().await?;
Ok(())
}

View File

@@ -0,0 +1,45 @@
use crate::fixtures::{check, world::AppWorld};
use anyhow::{Ok, Result};
use cucumber::then;
#[then(regex = r"^I see the result is empty$")]
async fn i_see_the_result_is_empty(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
check::result_text_is(client, "").await?;
Ok(())
}
#[then(regex = r"^I see the result is the string (.*)$")]
async fn i_see_the_result_is_the_string(
world: &mut AppWorld,
text: String,
) -> Result<()> {
let client = &world.client;
check::result_text_is(client, &text).await?;
Ok(())
}
#[then(regex = r"^I see the navbar$")]
async fn i_see_the_navbar(world: &mut AppWorld) -> Result<()> {
let client = &world.client;
check::element_exists(client, "nav").await?;
Ok(())
}
#[then(regex = r"^I see ([\d\w]+) is selected$")]
async fn i_see_the_select(world: &mut AppWorld, id: String) -> Result<()> {
let client = &world.client;
check::select_option_is_selected(client, &id).await?;
Ok(())
}
#[then(regex = r"^I see the value of (\w+) is (.*)$")]
async fn i_see_the_value(
world: &mut AppWorld,
id: String,
value: String,
) -> Result<()> {
let client = &world.client;
check::element_value_is(client, &id, &value).await?;
Ok(())
}

View File

@@ -0,0 +1,39 @@
pub mod action_steps;
pub mod check_steps;
use anyhow::Result;
use cucumber::World;
use fantoccini::{
error::NewSessionError, wd::Capabilities, Client, ClientBuilder,
};
pub const HOST: &str = "http://127.0.0.1:3000";
#[derive(Debug, World)]
#[world(init = Self::new)]
pub struct AppWorld {
pub client: Client,
}
impl AppWorld {
async fn new() -> Result<Self, anyhow::Error> {
let webdriver_client = build_client().await?;
Ok(Self {
client: webdriver_client,
})
}
}
async fn build_client() -> Result<Client, NewSessionError> {
let mut cap = Capabilities::new();
let arg = serde_json::from_str("{\"args\": [\"-headless\"]}").unwrap();
cap.insert("goog:chromeOptions".to_string(), arg);
let client = ClientBuilder::native()
.capabilities(cap)
.connect("http://localhost:4444")
.await?;
Ok(client)
}

View File

@@ -0,0 +1,79 @@
use crate::{
issue_4005::Routes4005, issue_4088::Routes4088, issue_4217::Routes4217,
issue_4285::Routes4285, issue_4296::Routes4296, pr_4015::Routes4015,
pr_4091::Routes4091,
};
use leptos::prelude::*;
use leptos_meta::{MetaTags, *};
use leptos_router::{
components::{Route, Router, Routes},
path,
};
pub fn shell(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=options.clone()/>
<HydrationScripts options/>
<MetaTags/>
</head>
<body>
<App/>
</body>
</html>
}
}
#[component]
pub fn App() -> impl IntoView {
provide_meta_context();
let fallback = || view! { "Page not found." }.into_view();
let (_, set_is_routing) = signal(false);
view! {
<Stylesheet id="leptos" href="/pkg/regression.css"/>
<Router set_is_routing>
<main>
<Routes fallback>
<Route path=path!("") view=HomePage/>
<Routes4091/>
<Routes4015/>
<Routes4088/>
<Routes4217/>
<Routes4005/>
<Routes4285/>
<Routes4296/>
</Routes>
</main>
</Router>
}
}
#[server]
async fn server_call() -> Result<(), ServerFnError> {
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
Ok(())
}
#[component]
fn HomePage() -> impl IntoView {
view! {
<Title text="Regression Tests"/>
<h1>"Listing of regression tests"</h1>
<nav>
<ul>
<li><a href="/4091/">"4091"</a></li>
<li><a href="/4015/">"4015"</a></li>
<li><a href="/4088/">"4088"</a></li>
<li><a href="/4217/">"4217"</a></li>
<li><a href="/4005/">"4005"</a></li>
<li><a href="/4285/">"4285"</a></li>
<li><a href="/4296/">"4296"</a></li>
</ul>
</nav>
}
}

View File

@@ -0,0 +1,24 @@
use leptos::prelude::*;
#[allow(unused_imports)]
use leptos_router::{
components::Route, path, MatchNestedRoutes, NavigateOptions,
};
#[component]
pub fn Routes4005() -> impl MatchNestedRoutes + Clone {
view! {
<Route path=path!("4005") view=Issue4005/>
}
.into_inner()
}
#[component]
fn Issue4005() -> impl IntoView {
view! {
<select id="select" prop:value="2">
<option value="1">"Option 1"</option>
<option value="2">"Option 2"</option>
<option value="3">"Option 3"</option>
</select>
}
}

View File

@@ -0,0 +1,119 @@
use leptos::{either::Either, prelude::*};
#[allow(unused_imports)]
use leptos_router::{
components::{Outlet, ParentRoute, Redirect, Route},
path, MatchNestedRoutes, NavigateOptions,
};
use serde::{Deserialize, Serialize};
#[component]
pub fn Routes4088() -> impl MatchNestedRoutes + Clone {
view! {
<ParentRoute path=path!("4088") view=|| view!{ <LoggedIn/> }>
<ParentRoute path=path!("") view=||view!{<AssignmentsSelector/>}>
<Route path=path!("/:team_id") view=||view!{<AssignmentsForTeam/>} />
<Route path=path!("") view=||view!{ <p>No class selected</p> }/>
</ParentRoute>
</ParentRoute>
}
.into_inner()
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserInfo {
pub id: usize,
}
#[server]
pub async fn get_user_info() -> Result<Option<UserInfo>, ServerFnError> {
Ok(Some(UserInfo { id: 42 }))
}
#[component]
pub fn LoggedIn() -> impl IntoView {
let user_info_resource =
Resource::new(|| (), move |_| async { get_user_info().await });
view! {
<Transition fallback=move || view!{
"loading"
}
>
{move || {
user_info_resource.get()
.map(|a|
match a {
Ok(Some(a)) => Either::Left(view! {
<LoggedInContent user_info={a} />
}),
_ => Either::Right(view!{
<Redirect path="/not_logged_in"/>
})
})
}}
</Transition>
}
}
#[component]
/// Component which provides UserInfo and renders it's child
/// Can also contain some code to check for specific situations (e.g. privacy policies accepted or not? redirect if needed...)
pub fn LoggedInContent(user_info: UserInfo) -> impl IntoView {
provide_context(user_info.clone());
if user_info.id == 42 {
Either::Left(Outlet())
} else {
Either::Right(
view! { <Redirect path="/somewhere" options={NavigateOptions::default()}/> },
)
}
}
#[component]
/// This component also uses Outlet (so nested Outlet)
fn AssignmentsSelector() -> impl IntoView {
let user_info = use_context::<UserInfo>().expect("user info not provided");
view! {
<p>"Assignments for user with ID: "{user_info.id}</p>
<ul id="nav">
<li><a href="/4088/1">"Class 1"</a></li>
<li><a href="/4088/2">"Class 2"</a></li>
<li><a href="/4088/3">"Class 3"</a></li>
</ul>
<Outlet />
}
}
#[component]
fn AssignmentsForTeam() -> impl IntoView {
// THIS FAILS -> Because of the nested outlet in LoggedInContent > AssignmentsSelector?
// It did not fail when LoggedIn did not use a resource and transition (but a hardcoded UserInfo in the component)
let user_info = use_context::<UserInfo>().expect("user info not provided");
let items = vec!["Assignment 1", "Assignment 2", "Assignment 3"];
view! {
<p id="result">"Assignments for team of user with id " {user_info.id}</p>
<ul>
{
items.into_iter().map(|item| {
view! {
<Assignment name=item.to_string() />
}
}).collect_view()
}
</ul>
}
}
#[component]
fn Assignment(name: String) -> impl IntoView {
let user_info = use_context::<UserInfo>().expect("user info not provided");
view! {
<li>{name}" "{user_info.id}</li>
}
}

View File

@@ -0,0 +1,24 @@
use leptos::prelude::*;
#[allow(unused_imports)]
use leptos_router::{
components::Route, path, MatchNestedRoutes, NavigateOptions,
};
#[component]
pub fn Routes4217() -> impl MatchNestedRoutes + Clone {
view! {
<Route path=path!("4217") view=Issue4217/>
}
.into_inner()
}
#[component]
fn Issue4217() -> impl IntoView {
view! {
<select multiple=true>
<option id="option1" value="1" selected>"Option 1"</option>
<option id="option2" value="2" selected>"Option 2"</option>
<option id="option3" value="3" selected>"Option 3"</option>
</select>
}
}

View File

@@ -0,0 +1,49 @@
use leptos::prelude::*;
use leptos_router::LazyRoute;
#[allow(unused_imports)]
use leptos_router::{
components::Route, path, Lazy, MatchNestedRoutes, NavigateOptions,
};
#[component]
pub fn Routes4285() -> impl MatchNestedRoutes + Clone {
view! {
<Route path=path!("4285") view={Lazy::<Issue4285>::new()}/>
}
.into_inner()
}
struct Issue4285 {
data: Resource<Result<i32, ServerFnError>>,
}
impl LazyRoute for Issue4285 {
fn data() -> Self {
Self {
data: Resource::new(|| (), |_| slow_call()),
}
}
async fn view(this: Self) -> AnyView {
let Issue4285 { data } = this;
view! {
<Suspense>
{move || {
Suspend::new(async move {
let data = data.await;
view! {
<p id="result">{data}</p>
}
})
}}
</Suspense>
}
.into_any()
}
}
#[server]
async fn slow_call() -> Result<i32, ServerFnError> {
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
Ok(42)
}

View File

@@ -0,0 +1,36 @@
use leptos::prelude::*;
#[allow(unused_imports)]
use leptos_router::{
components::Route, path, Lazy, MatchNestedRoutes, NavigateOptions,
};
use leptos_router::{hooks::use_query_map, LazyRoute};
#[component]
pub fn Routes4296() -> impl MatchNestedRoutes + Clone {
view! {
<Route path=path!("4296") view={Lazy::<Issue4296>::new()}/>
}
.into_inner()
}
struct Issue4296 {
query: Signal<Option<String>>,
}
impl LazyRoute for Issue4296 {
fn data() -> Self {
let query = use_query_map();
let query = Signal::derive(move || query.read().get("q"));
Self { query }
}
async fn view(this: Self) -> AnyView {
let Issue4296 { query } = this;
view! {
<a href="?q=abc">"abc"</a>
<a href="?q=def">"def"</a>
<p id="result">{move || format!("{:?}", query.get())}</p>
}
.into_any()
}
}

View File

@@ -0,0 +1,16 @@
pub mod app;
mod issue_4005;
mod issue_4088;
mod issue_4217;
mod issue_4285;
mod issue_4296;
mod pr_4015;
mod pr_4091;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use app::*;
console_error_panic_hook::set_once();
leptos::mount::hydrate_body(App);
}

View File

@@ -0,0 +1,37 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::Router;
use leptos::prelude::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use regression::app::{shell, App};
let conf = get_configuration(None).unwrap();
let addr = conf.leptos_options.site_addr;
let leptos_options = conf.leptos_options;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(App);
let app = Router::new()
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())
})
.fallback(leptos_axum::file_and_error_handler(shell))
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
println!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
#[cfg(not(feature = "ssr"))]
pub fn main() {
// no client-side main function
// unless we want this to work with e.g., Trunk for pure client-side testing
// see lib.rs for hydration function instead
}

View File

@@ -0,0 +1,29 @@
use leptos::{context::Provider, prelude::*};
use leptos_router::{
components::{ParentRoute, Route},
nested_router::Outlet,
path,
};
#[component]
pub fn Routes4015() -> impl leptos_router::MatchNestedRoutes + Clone {
view! {
<ParentRoute path=path!("4015") view=|| view! {
<Provider value=42i32>
<Outlet/>
</Provider>
}>
<Route path=path!("") view=Child/>
</ParentRoute>
}
.into_inner()
}
#[component]
fn Child() -> impl IntoView {
let value = use_context::<i32>();
view! {
<p id="result">{format!("{value:?}")}</p>
}
}

View File

@@ -0,0 +1,68 @@
use leptos::{context::Provider, prelude::*};
use leptos_router::{
components::{ParentRoute, Route, A},
nested_router::Outlet,
path,
};
// FIXME This should be a set rather than a naive vec for push and pop, as
// it may be possible for unexpected token be popped/pushed on multi-level
// navigation. For basic naive tests it should be Fine(TM).
#[derive(Clone)]
struct Expectations(Vec<&'static str>);
#[component]
pub fn Routes4091() -> impl leptos_router::MatchNestedRoutes + Clone {
view! {
<ParentRoute path=path!("4091") view=Container>
<Route path=path!("") view=Root/>
<Route path=path!("test1") view=Test1/>
</ParentRoute>
}
.into_inner()
}
#[component]
fn Container() -> impl IntoView {
let rw_signal = RwSignal::new(Expectations(Vec::new()));
provide_context(rw_signal);
view! {
<nav id="nav">
<ul>
<li><A href="/">"Home"</A></li>
<li><A href="./">"4091 Home"</A></li>
<li><A href="test1">"test1"</A></li>
</ul>
</nav>
<div id="result">{move || {
rw_signal.with(|ex| ex.0.iter().fold(String::new(), |a, b| a + b + " "))
}}</div>
<Provider value=rw_signal>
<Outlet/>
</Provider>
}
}
#[component]
fn Root() -> impl IntoView {
view! {
<div>"This is Root"</div>
}
}
#[component]
fn Test1() -> impl IntoView {
let signal = expect_context::<RwSignal<Expectations>>();
on_cleanup(move || {
signal.update(|ex| {
ex.0.pop();
});
});
view! {
{move || signal.update(|ex| ex.0.push("Test1"))}
<div>"This is Test1"</div>
}
}

View File

@@ -0,0 +1,3 @@
body {
font-family: sans-serif;
}

View File

@@ -29,7 +29,7 @@ tower-http = { version = "0.6.2", features = [
"trace",
], optional = true }
tokio = { version = "1.39", features = ["full"], optional = true }
thiserror = "2.0.11"
thiserror = "2.0.12"
wasm-bindgen = "0.2.93"
serde_toml = "0.0.1"
toml = "0.8.19"

View File

@@ -2,8 +2,6 @@
name = "ssr_modes"
version = "0.1.0"
edition = "2021"
# std::sync::LazyLock is stabilized in Rust version 1.80.0
rust-version = "1.80.0"
[lib]
crate-type = ["cdylib", "rlib"]
@@ -19,7 +17,7 @@ leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_router = { path = "../../router" }
log = "0.4.22"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
thiserror = "2.0.12"
tokio = { version = "1.39", features = ["time"] }
wasm-bindgen = "0.2.93"

View File

@@ -2,8 +2,6 @@
name = "ssr_modes_axum"
version = "0.1.0"
edition = "2021"
# std::sync::LazyLock is stabilized in Rust version 1.80.0
rust-version = "1.80.0"
[lib]
crate-type = ["cdylib", "rlib"]
@@ -19,7 +17,7 @@ leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router" }
log = "0.4.22"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
thiserror = "2.0.12"
axum = { version = "0.8.1", optional = true }
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.5.2", features = ["fs"], optional = true }

View File

@@ -17,7 +17,7 @@ leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router" }
log = "0.4.22"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
thiserror = "2.0.12"
axum = { version = "0.8.1", optional = true }
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.5.2", features = ["fs"], optional = true }

View File

@@ -159,7 +159,7 @@ fn TodoRow(
view! {
<li style:text-decoration=move || {
status.done().then_some("line-through").unwrap_or_default()
if status.done() { "line-through" } else { Default::default() }
}>
<p

View File

@@ -20,7 +20,7 @@ tokio = { version = "1.39", features = [
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
wasm-bindgen = "0.2.93"
thiserror = "1.0"
thiserror = "2.0.12"
tracing = { version = "0.1.40", optional = true }
http = "1.1"

View File

@@ -20,7 +20,7 @@ leptos_actix = { path = "../../integrations/actix", optional = true }
log = "0.4.22"
simple_logger = "5.0"
gloo = { git = "https://github.com/rustwasm/gloo" }
sqlx = { version = "0.8.0", features = [
sqlx = { version = "0.8.6", features = [
"runtime-tokio-rustls",
"sqlite",
], optional = true }
@@ -44,12 +44,12 @@ denylist = ["actix-files", "actix-web", "leptos_actix", "sqlx"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name = "todo_app_sqlite"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
site-root = "target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
# Defaults to pkg
# Defaults to pkg
site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
style-file = "./style.css"

View File

@@ -20,11 +20,11 @@ axum = { version = "0.8.1", optional = true }
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
tokio = { version = "1.39", features = ["full"], optional = true }
sqlx = { version = "0.8.0", features = [
sqlx = { version = "0.8.6", features = [
"runtime-tokio-rustls",
"sqlite",
], optional = true }
thiserror = "1.0"
thiserror = "2.0.12"
wasm-bindgen = "0.2.93"
[features]

View File

@@ -20,11 +20,11 @@ tower = { version = "0.5.1", features = ["util"], optional = true }
tower-http = { version = "0.6.1", features = ["fs"], optional = true }
tokio = { version = "1.39", features = ["full"], optional = true }
http = { version = "1.1" }
sqlx = { version = "0.8.0", features = [
sqlx = { version = "0.8.6", features = [
"runtime-tokio-rustls",
"sqlite",
], optional = true }
thiserror = "2.0"
thiserror = "2.0.12"
wasm-bindgen = "0.2.93"
[features]

View File

@@ -17,7 +17,7 @@ simple_logger = "5.0"
serde = { version = "1.0", features = ["derive"] }
axum = { version = "0.8.1", optional = true }
tokio = { version = "1.39", features = ["full"], optional = true }
thiserror = "2.0"
thiserror = "2.0.12"
wasm-bindgen = "0.2.100"
[features]

View File

@@ -4,14 +4,14 @@ authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Actix integrations for the Leptos web framework."
version = { workspace = true }
version = "0.8.5"
rust-version.workspace = true
edition.workspace = true
[dependencies]
actix-http = { workspace = true, default-features = true }
actix-files = { workspace = true, default-features = true }
actix-web = { workspace = true, default-features = true }
actix-web = { workspace = true, default-features = false }
futures = { workspace = true, default-features = true }
any_spawner = { workspace = true, features = ["tokio"] }
hydration_context = { workspace = true }
@@ -20,12 +20,12 @@ leptos_integration_utils = { workspace = true }
leptos_macro = { workspace = true, features = ["actix"] }
leptos_meta = { workspace = true, features = ["nonce"] }
leptos_router = { workspace = true, features = ["ssr"] }
server_fn = { workspace = true, features = ["actix"] }
server_fn = { workspace = true, features = ["actix-no-default"] }
tachys = { workspace = true }
serde_json = { workspace = true , default-features = true }
serde_json = { workspace = true, default-features = true }
parking_lot = { workspace = true, default-features = true }
tracing = { optional = true , workspace = true, default-features = true }
tokio = { features = ["rt", "fs"] , workspace = true, default-features = true }
tracing = { optional = true, workspace = true, default-features = true }
tokio = { features = ["rt", "fs"], workspace = true, default-features = true }
send_wrapper = { workspace = true, default-features = true }
dashmap = { workspace = true, default-features = true }
@@ -33,6 +33,8 @@ dashmap = { workspace = true, default-features = true }
rustdoc-args = ["--generate-link-to-definition"]
[features]
default = ["actix-default"]
actix-default = ["actix-web/default"]
islands-router = ["tachys/islands"]
tracing = ["dep:tracing"]

View File

@@ -282,6 +282,7 @@ pub fn redirect(path: &str) {
/// // call ServerFn::register() for each of the server functions you've defined
/// }
///
/// # #[cfg(feature = "default")]
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// // make sure you actually register your server functions
@@ -297,6 +298,8 @@ pub fn redirect(path: &str) {
/// .run()
/// .await
/// }
/// # #[cfg(not(feature = "default"))]
/// # fn main() {}
/// ```
///
/// ## Provided Context Types
@@ -442,6 +445,7 @@ pub fn handle_server_fns_with_context(
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # #[cfg(feature = "default")]
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// let conf = get_configuration(Some("Cargo.toml")).unwrap();
@@ -461,6 +465,8 @@ pub fn handle_server_fns_with_context(
/// .run()
/// .await
/// }
/// # #[cfg(not(feature = "default"))]
/// # fn main() {}
/// ```
///
/// ## Provided Context Types
@@ -499,6 +505,7 @@ where
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # #[cfg(feature = "default")]
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// let conf = get_configuration(Some("Cargo.toml")).unwrap();
@@ -521,6 +528,9 @@ where
/// .run()
/// .await
/// }
///
/// # #[cfg(not(feature = "default"))]
/// # fn main() {}
/// ```
///
/// ## Provided Context Types
@@ -557,6 +567,7 @@ where
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # #[cfg(feature = "default")]
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// let conf = get_configuration(Some("Cargo.toml")).unwrap();
@@ -576,6 +587,8 @@ where
/// .run()
/// .await
/// }
/// # #[cfg(not(feature = "default"))]
/// # fn main() {}
/// ```
///
/// ## Provided Context Types

View File

@@ -4,7 +4,7 @@ authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Axum integrations for the Leptos web framework."
version = "0.8.2"
version = "0.8.6"
rust-version.workspace = true
edition.workspace = true
@@ -13,7 +13,7 @@ any_spawner = { workspace = true, features = ["tokio"] }
hydration_context = { workspace = true }
axum = { default-features = false, features = [
"matched-path",
] , workspace = true }
], workspace = true }
dashmap = { workspace = true, default-features = true }
futures = { workspace = true, default-features = true }
leptos = { workspace = true, features = ["nonce", "ssr"] }
@@ -24,14 +24,17 @@ leptos_router = { workspace = true, features = ["ssr"] }
leptos_integration_utils = { workspace = true }
tachys = { workspace = true }
parking_lot = { workspace = true, default-features = true }
tokio = { default-features = false , workspace = true }
tower = { features = ["util"] , workspace = true, default-features = true }
tokio = { default-features = false, workspace = true }
tower = { features = ["util"], workspace = true, default-features = true }
tower-http = { workspace = true, default-features = true }
tracing = { optional = true , workspace = true, default-features = true }
tracing = { optional = true, workspace = true, default-features = true }
[dev-dependencies]
axum = { workspace = true, default-features = true }
tokio = { features = ["net", "rt-multi-thread"] , workspace = true, default-features = true }
tokio = { features = [
"net",
"rt-multi-thread",
], workspace = true, default-features = true }
[features]
wasm = []

View File

@@ -1177,7 +1177,7 @@ where
generate_route_list_with_exclusions_and_ssg(app_fn, None).0
}
/// Generates a list of all routes defined in Leptos's Router in your app. We can then use t.clone()his to automatically
/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically
/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element
/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths.
#[cfg_attr(
@@ -2061,10 +2061,12 @@ where
req,
|app, chunks, _supports_ooo| {
Box::pin(async move {
let app = app
.to_html_stream_in_order()
.collect::<String>()
.await;
let app = if cfg!(feature = "islands-router") {
app.to_html_stream_in_order_branching()
} else {
app.to_html_stream_in_order()
};
let app = app.collect::<String>().await;
let chunks = chunks();
Box::pin(once(async move { app }).chain(chunks))
as PinnedStream<String>

View File

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

View File

@@ -3,12 +3,14 @@
use futures::{stream::once, Stream, StreamExt};
use hydration_context::{SharedContext, SsrSharedContext};
use leptos::{
context::provide_context,
nonce::use_nonce,
prelude::ReadValue,
reactive::owner::{Owner, Sandboxed},
IntoView,
IntoView, PrefetchLazyFn, WasmSplitManifest,
};
use leptos_config::LeptosOptions;
use leptos_meta::ServerMetaContextOutput;
use leptos_meta::{Link, ServerMetaContextOutput};
use std::{future::Future, pin::Pin, sync::Arc};
pub type PinnedStream<T> = Pin<Box<dyn Stream<Item = T> + Send>>;
@@ -41,6 +43,8 @@ pub trait ExtendResponse: Sized {
IV: IntoView + 'static,
{
async move {
let prefetches = PrefetchLazyFn::default();
let (owner, stream) = build_response(
app_fn,
additional_context,
@@ -48,6 +52,8 @@ pub trait ExtendResponse: Sized {
supports_ooo,
);
owner.with(|| provide_context(prefetches.clone()));
let sc = owner.shared_context().unwrap();
let stream = stream.await.ready_chunks(32).map(|n| n.join(""));
@@ -56,6 +62,40 @@ pub trait ExtendResponse: Sized {
pending.await;
}
if !prefetches.0.read_value().is_empty() {
use leptos::prelude::*;
let nonce =
use_nonce().map(|n| n.to_string()).unwrap_or_default();
if let Some(manifest) = use_context::<WasmSplitManifest>() {
let (pkg_path, manifest) = &*manifest.0.read_value();
let prefetches = prefetches.0.read_value();
let all_prefetches = prefetches.iter().flat_map(|key| {
manifest.get(*key).into_iter().flatten()
});
for module in all_prefetches {
// to_html() on leptos_meta components registers them with the meta context,
// rather than returning HTML directly
_ = view! {
<Link
rel="preload"
href=format!("{pkg_path}/{module}.wasm")
as_="fetch"
type_="application/wasm"
crossorigin=nonce.clone()
/>
}
.to_html();
}
_ = view! {
<Link rel="modulepreload" href=format!("{pkg_path}/__wasm_split.js") crossorigin=nonce/>
}
.to_html();
}
}
let mut stream = Box::pin(
meta_context.inject_meta_context(stream).await.then({
let sc = Arc::clone(&sc);

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos"
version = { workspace = true }
version = "0.8.8"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
@@ -24,14 +24,14 @@ leptos_hot_reload = { workspace = true }
leptos_macro = { workspace = true }
leptos_server = { workspace = true, features = ["tachys"] }
leptos_config = { workspace = true }
leptos-spin-macro = { optional = true , workspace = true, default-features = true }
leptos-spin-macro = { optional = true, workspace = true, default-features = true }
oco_ref = { workspace = true }
or_poisoned = { workspace = true }
paste = { workspace = true, default-features = true }
rand = { optional = true , workspace = true, default-features = true }
rand = { optional = true, workspace = true, default-features = true }
# NOTE: While not used directly, `getrandom`'s `wasm_js` feature is needed when `rand` is used on WASM to
# avoid a compilation error
getrandom = { optional = true , workspace = true, default-features = true }
getrandom = { optional = true, workspace = true, default-features = true }
reactive_graph = { workspace = true, features = ["serde"] }
rustc-hash = { workspace = true, default-features = true }
tachys = { workspace = true, features = [
@@ -44,7 +44,7 @@ tracing = { optional = true, workspace = true, default-features = true }
typed-builder = { workspace = true, default-features = true }
typed-builder-macro = { workspace = true, default-features = true }
serde = { workspace = true, default-features = true }
serde_json = { optional = true, workspace = true, default-features = true }
serde_json = { workspace = true, default-features = true }
server_fn = { workspace = true, features = ["form-redirects", "browser"] }
web-sys = { features = [
"ShadowRoot",
@@ -52,10 +52,12 @@ web-sys = { features = [
"ShadowRootMode",
], workspace = true, default-features = true }
wasm-bindgen = { workspace = true, default-features = true }
wasm-bindgen-futures = { workspace = true, default-features = true }
serde_qs = { workspace = true, default-features = true }
slotmap = { workspace = true, default-features = true }
futures = { workspace = true, default-features = true }
send_wrapper = { workspace = true, default-features = true }
wasm_split_helpers.workspace = true
[features]
hydration = [
@@ -93,7 +95,7 @@ tracing = [
]
nonce = ["base64", "rand", "dep:getrandom"]
spin = ["leptos-spin-macro"]
islands = ["leptos_macro/islands", "dep:serde_json"]
islands = ["leptos_macro/islands"]
trace-component-props = [
"leptos_macro/trace-component-props",
"leptos_dom/trace-component-props",
@@ -102,7 +104,10 @@ delegation = ["tachys/delegation"]
islands-router = ["tachys/mark_branches"]
[dev-dependencies]
tokio = { features = ["rt-multi-thread", "macros"] , workspace = true, default-features = true }
tokio = { features = [
"rt-multi-thread",
"macros",
], workspace = true, default-features = true }
tokio-test = { workspace = true, default-features = true }
any_spawner = { workspace = true, features = ["futures-executor", "tokio"] }

View File

@@ -157,6 +157,14 @@ impl<T: IntoView + 'static, A: Attribute> RenderHtml
self.children.hydrate::<FROM_SERVER>(cursor, position)
}
async fn hydrate_async(
self,
cursor: &leptos::tachys::hydration::Cursor,
position: &leptos::tachys::view::PositionState,
) -> Self::State {
self.children.hydrate_async(cursor, position).await
}
fn into_owned(self) -> Self::Owned {
AttributeInterceptorInner {
children_builder: self.children_builder,

View File

@@ -262,6 +262,16 @@ where
}
}
impl<C> From<View<C>> for ViewFn
where
C: Clone + Send + Sync + 'static,
View<C>: IntoAny,
{
fn from(value: View<C>) -> Self {
Self(Arc::new(move || value.clone().into_any()))
}
}
impl ViewFn {
/// Execute the wrapped function
pub fn run(&self) -> AnyView {
@@ -289,6 +299,16 @@ where
}
}
impl<C> From<View<C>> for ViewFnOnce
where
C: Send + Sync + 'static,
View<C>: IntoAny,
{
fn from(value: View<C>) -> Self {
Self(Box::new(move || value.into_any()))
}
}
impl ViewFnOnce {
/// Execute the wrapped function
pub fn run(self) -> AnyView {

View File

@@ -2,6 +2,7 @@ use crate::{children::TypedChildren, IntoView};
use futures::{channel::oneshot, future::join_all};
use hydration_context::{SerializedDataId, SharedContext};
use leptos_macro::component;
use or_poisoned::OrPoisoned;
use reactive_graph::{
computed::ArcMemo,
effect::RenderEffect,
@@ -10,7 +11,12 @@ use reactive_graph::{
traits::{Get, Update, With, WithUntracked, WriteValue},
};
use rustc_hash::FxHashMap;
use std::{collections::VecDeque, fmt::Debug, mem, sync::Arc};
use std::{
collections::VecDeque,
fmt::Debug,
mem,
sync::{Arc, Mutex},
};
use tachys::{
html::attribute::{any_attribute::AnyAttribute, Attribute},
hydration::Cursor,
@@ -508,6 +514,79 @@ where
)
}
async fn hydrate_async(
self,
cursor: &Cursor,
position: &PositionState,
) -> Self::State {
let mut children = Some(self.children);
let hook = Arc::clone(&self.hook);
let cursor = cursor.to_owned();
let position = position.to_owned();
let fallback_fn = Arc::new(Mutex::new(self.fallback));
let initial = {
let errors_empty = self.errors_empty.clone();
let errors = self.errors.clone();
let fallback_fn = Arc::clone(&fallback_fn);
async move {
let children = children.take().unwrap();
let (children, fallback) = if errors_empty.get() {
(children.hydrate_async(&cursor, &position).await, None)
} else {
let children = children.build();
let fallback =
(fallback_fn.lock().or_poisoned())(errors.clone());
let fallback =
fallback.hydrate_async(&cursor, &position).await;
(children, Some(fallback))
};
ErrorBoundaryViewState { children, fallback }
}
};
RenderEffect::new_with_async_value(
move |prev: Option<
ErrorBoundaryViewState<Chil::State, Fal::State>,
>| {
let _hook = throw_error::set_error_hook(Arc::clone(&hook));
if let Some(mut state) = prev {
match (self.errors_empty.get(), &mut state.fallback) {
// no errors, and was showing fallback
(true, Some(fallback)) => {
fallback.insert_before_this(&mut state.children);
state.fallback.unmount();
state.fallback = None;
}
// yes errors, and was showing children
(false, None) => {
state.fallback = Some(
(fallback_fn.lock().or_poisoned())(
self.errors.clone(),
)
.build(),
);
state
.children
.insert_before_this(&mut state.fallback);
state.children.unmount();
}
// either there were no errors, and we were already showing the children
// or there are errors, but we were already showing the fallback
// in either case, rebuilding doesn't require us to do anything
_ => {}
}
state
} else {
unreachable!()
}
},
initial,
)
.await
}
fn into_owned(self) -> Self::Owned {
self
}

View File

@@ -8,46 +8,49 @@
c();
}
}
function hydrateIslands(rootNode, mod) {
function traverse(node) {
async function hydrateIslands(rootNode, mod) {
async function traverse(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
const tag = node.tagName.toLowerCase();
if(tag === 'leptos-island') {
const children = [];
const id = node.dataset.component || null;
hydrateIsland(node, id, mod);
await hydrateIsland(node, id, mod);
for(const child of node.children) {
traverse(child, children);
await traverse(child, children);
}
} else {
if (tag === 'leptos-children') {
MOST_RECENT_CHILDREN_CB.push(node.$$on_hydrate);
for(const child of node.children) {
traverse(child);
await traverse(child);
};
// un-set the "most recent children"
MOST_RECENT_CHILDREN_CB.pop();
} else {
for(const child of node.children) {
traverse(child);
await traverse(child);
};
}
}
}
}
traverse(rootNode);
await traverse(rootNode);
}
function hydrateIsland(el, id, mod) {
async function hydrateIsland(el, id, mod) {
const islandFn = mod[id];
if (islandFn) {
const children_cb = MOST_RECENT_CHILDREN_CB[MOST_RECENT_CHILDREN_CB.length-1];
if (children_cb) {
children_cb();
}
islandFn(el);
const res = islandFn(el);
if (res && res.then) {
await res;
}
} else {
console.warn(`Could not find WASM function for the island ${id}.`);
}

View File

@@ -1,8 +1,9 @@
#![allow(clippy::needless_lifetimes)]
use crate::prelude::*;
use crate::{prelude::*, WasmSplitManifest};
use leptos_config::LeptosOptions;
use leptos_macro::{component, view};
use std::{path::PathBuf, sync::OnceLock};
/// Inserts auto-reloading code used in `cargo-leptos`.
///
@@ -58,6 +59,29 @@ pub fn HydrationScripts(
#[prop(optional, into)]
root: Option<String>,
) -> impl IntoView {
static SPLIT_MANIFEST: OnceLock<Option<WasmSplitManifest>> =
OnceLock::new();
if let Some(splits) = SPLIT_MANIFEST.get_or_init(|| {
let root = root.clone().unwrap_or_default();
let site_dir = &options.site_root;
let pkg_dir = &options.site_pkg_dir;
let path = PathBuf::from(site_dir.to_string());
let path = path
.join(pkg_dir.to_string())
.join("__wasm_split_manifest.json");
let file = std::fs::read_to_string(path).ok()?;
let manifest = WasmSplitManifest(ArcStoredValue::new((
format!("{root}/{pkg_dir}"),
serde_json::from_str(&file).expect("could not read manifest file"),
)));
Some(manifest)
}) {
provide_context(splits.clone());
}
let mut js_file_name = options.output_name.to_string();
let mut wasm_file_name = options.output_name.to_string();
if options.hash_files {
@@ -112,7 +136,7 @@ pub fn HydrationScripts(
let root = root.unwrap_or_default();
view! {
<link rel="modulepreload" href=format!("{root}/{pkg_path}/{js_file_name}.js") nonce=nonce.clone()/>
<link rel="modulepreload" href=format!("{root}/{pkg_path}/{js_file_name}.js") crossorigin=nonce.clone()/>
<link
rel="preload"
href=format!("{root}/{pkg_path}/{wasm_file_name}.wasm")

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