mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 11:21:55 -05:00
Compare commits
1 Commits
3872
...
document-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d7fe46661 |
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
run: cargo fmt --all
|
||||
- name: Clippy the workspace
|
||||
run: cargo all-features clippy --allow-dirty --fix --lib --no-deps
|
||||
- uses: autofix-ci/action@v1.3.2
|
||||
- uses: autofix-ci/action@v1.3.1
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
fail-fast: false
|
||||
|
||||
249
Cargo.lock
generated
249
Cargo.lock
generated
@@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -205,7 +205,7 @@ dependencies = [
|
||||
"actix-router",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -233,9 +233,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
@@ -327,7 +327,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -344,7 +344,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -367,7 +367,7 @@ dependencies = [
|
||||
"manyhow",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -383,7 +383,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote-use",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -509,9 +509,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.18.1"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
@@ -533,7 +533,7 @@ checksum = "efb7846e0cb180355c2dec69e721edafa36919850f1a9f52ffba4ebc0393cb71"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -559,15 +559,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.10"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
|
||||
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.27"
|
||||
version = "1.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -586,9 +586,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
@@ -850,7 +850,7 @@ checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -863,7 +863,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -883,7 +883,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
@@ -905,7 +905,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1006,9 +1006,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
@@ -1120,7 +1120,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1172,7 +1172,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1356,9 +1356,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.4"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
@@ -1382,9 +1382,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "html-escape"
|
||||
@@ -1470,6 +1470,7 @@ version = "0.3.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"or_poisoned",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
@@ -1499,9 +1500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.7"
|
||||
version = "0.27.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||
checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d"
|
||||
dependencies = [
|
||||
"http 1.3.1",
|
||||
"hyper",
|
||||
@@ -1532,9 +1533,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.14"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
|
||||
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@@ -1674,7 +1675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.4",
|
||||
"hashbrown 0.15.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1811,7 +1812,7 @@ dependencies = [
|
||||
"http 1.3.1",
|
||||
"proc-macro-error",
|
||||
"server_fn_macro 0.6.15",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1830,6 +1831,7 @@ dependencies = [
|
||||
"leptos_macro",
|
||||
"leptos_meta",
|
||||
"leptos_router",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"send_wrapper",
|
||||
"serde_json",
|
||||
@@ -1853,6 +1855,7 @@ dependencies = [
|
||||
"leptos_macro",
|
||||
"leptos_meta",
|
||||
"leptos_router",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"server_fn",
|
||||
"tachys",
|
||||
@@ -1905,7 +1908,7 @@ dependencies = [
|
||||
"quote",
|
||||
"rstml",
|
||||
"serde",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -1945,7 +1948,7 @@ dependencies = [
|
||||
"serde",
|
||||
"server_fn",
|
||||
"server_fn_macro 0.8.2",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"tracing",
|
||||
"trybuild",
|
||||
"typed-builder",
|
||||
@@ -1959,6 +1962,7 @@ dependencies = [
|
||||
"futures",
|
||||
"indexmap",
|
||||
"leptos",
|
||||
"once_cell",
|
||||
"or_poisoned",
|
||||
"send_wrapper",
|
||||
"tracing",
|
||||
@@ -1977,6 +1981,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"leptos",
|
||||
"leptos_router_macro",
|
||||
"once_cell",
|
||||
"or_poisoned",
|
||||
"percent-encoding",
|
||||
"reactive_graph",
|
||||
@@ -1999,7 +2004,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2025,9 +2030,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.173"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "linear-map"
|
||||
@@ -2095,7 +2100,7 @@ dependencies = [
|
||||
"manyhow-macros",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2117,9 +2122,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
@@ -2145,7 +2150,7 @@ checksum = "d6c74ab4f1a0c0ab045260ee4727b23c00cc17e5eff5095262d08eef8c3c8d49"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2171,9 +2176,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -2186,7 +2191,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -2224,7 +2229,7 @@ checksum = "0ac7d860b767c6398e88fe93db73ce53eb496057aa6895ffa4d60cb02e1d1c6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2265,9 +2270,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.17.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
@@ -2299,9 +2304,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.73"
|
||||
version = "0.10.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -2320,7 +2325,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2331,9 +2336,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.109"
|
||||
version = "0.9.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -2409,7 +2414,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2469,12 +2474,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.34"
|
||||
version = "0.2.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
|
||||
checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2529,7 +2534,7 @@ dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2560,7 +2565,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
@@ -2582,7 +2587,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2668,7 +2673,7 @@ dependencies = [
|
||||
"proc-macro-utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2765,14 +2770,14 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -2823,9 +2828,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.20"
|
||||
version = "0.12.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813"
|
||||
checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@@ -2838,10 +2843,13 @@ dependencies = [
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
@@ -2888,7 +2896,7 @@ checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
"bytes",
|
||||
"hashbrown 0.15.4",
|
||||
"hashbrown 0.15.3",
|
||||
"indexmap",
|
||||
"munge",
|
||||
"ptr_meta",
|
||||
@@ -2907,7 +2915,7 @@ checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2942,16 +2950,16 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"syn_derive",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -3138,7 +3146,7 @@ checksum = "7ce26a84e3d8d10853301cf6a75c58132b8f5d5e8fee65949ea8dd7758d6760b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3160,7 +3168,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3198,9 +3206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.9"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -3239,7 +3247,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3263,6 +3271,7 @@ dependencies = [
|
||||
"inventory",
|
||||
"js-sys",
|
||||
"multer",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"postcard",
|
||||
"reqwest",
|
||||
@@ -3301,7 +3310,7 @@ dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
@@ -3314,7 +3323,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
@@ -3323,7 +3332,7 @@ name = "server_fn_macro_default"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"server_fn_macro 0.8.2",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3390,7 +3399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f62f06db0370222f7f498ef478fce9f8df5828848d1d3517e3331936d7074f55"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3413,9 +3422,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
@@ -3460,9 +3469,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.103"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3478,7 +3487,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3498,7 +3507,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3532,6 +3541,7 @@ dependencies = [
|
||||
"linear-map",
|
||||
"next_tuple",
|
||||
"oco_ref",
|
||||
"once_cell",
|
||||
"or_poisoned",
|
||||
"parking_lot",
|
||||
"paste",
|
||||
@@ -3623,7 +3633,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3634,7 +3644,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3726,7 +3736,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3801,9 +3811,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -3813,18 +3823,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
version = "0.22.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -3836,9 +3846,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
@@ -3858,13 +3868,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.6"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body",
|
||||
@@ -3910,20 +3919,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.29"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
@@ -3983,7 +3992,7 @@ checksum = "60d8d828da2a3d759d3519cdf29a5bac49c77d039ad36d0782edadbf9cd5415b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4107,9 +4116,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
@@ -4142,7 +4151,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4177,7 +4186,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4212,7 +4221,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4350,9 +4359,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.11"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -4404,7 +4413,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4425,7 +4434,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4445,7 +4454,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4485,7 +4494,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -42,7 +42,7 @@ exclude = ["benchmarks", "examples", "projects"]
|
||||
[workspace.package]
|
||||
version = "0.8.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.80"
|
||||
rust-version = "1.76"
|
||||
|
||||
[workspace.dependencies]
|
||||
# members
|
||||
@@ -85,6 +85,7 @@ 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" }
|
||||
rustc-hash = { default-features = false, version = "2.1.1" }
|
||||
once_cell = { default-features = false, version = "1.21.3" }
|
||||
actix-web = { default-features = false, version = "4.11.0" }
|
||||
tracing = { default-features = false, version = "0.1.41" }
|
||||
slotmap = { default-features = false, version = "1.0.7" }
|
||||
|
||||
@@ -23,6 +23,7 @@ leptos = { path = "../../leptos" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4.22"
|
||||
once_cell = "1.19"
|
||||
gloo-net = { version = "0.6.0" }
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
[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 = "1.0"
|
||||
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
|
||||
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,8 +0,0 @@
|
||||
extend = [
|
||||
{ path = "../cargo-make/main.toml" },
|
||||
{ path = "../cargo-make/cargo-leptos-webdriver-test.toml" },
|
||||
]
|
||||
|
||||
[env]
|
||||
|
||||
CLIENT_PROCESS_NAME = "regression"
|
||||
@@ -1,8 +0,0 @@
|
||||
# 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.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,18 +0,0 @@
|
||||
[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
|
||||
@@ -1,20 +0,0 @@
|
||||
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",
|
||||
"${@}",
|
||||
]
|
||||
@@ -1,34 +0,0 @@
|
||||
# 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
|
||||
```
|
||||
@@ -1,26 +0,0 @@
|
||||
@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
|
||||
@@ -1,30 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
17
examples/regression/e2e/tests/fixtures/action.rs
vendored
17
examples/regression/e2e/tests/fixtures/action.rs
vendored
@@ -1,17 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
13
examples/regression/e2e/tests/fixtures/check.rs
vendored
13
examples/regression/e2e/tests/fixtures/check.rs
vendored
@@ -1,13 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
21
examples/regression/e2e/tests/fixtures/find.rs
vendored
21
examples/regression/e2e/tests/fixtures/find.rs
vendored
@@ -1,21 +0,0 @@
|
||||
use anyhow::{Ok, Result};
|
||||
use fantoccini::{elements::Element, Client, Locator};
|
||||
|
||||
pub async fn text_at_id(client: &Client, id: &str) -> Result<String> {
|
||||
let element = client
|
||||
.wait()
|
||||
.for_element(Locator::Id(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)
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod action;
|
||||
pub mod check;
|
||||
pub mod find;
|
||||
pub mod world;
|
||||
@@ -1,47 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
use crate::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();
|
||||
view! {
|
||||
<Stylesheet id="leptos" href="/pkg/regression.css"/>
|
||||
<Router>
|
||||
<main>
|
||||
<Routes fallback>
|
||||
<Route path=path!("") view=HomePage/>
|
||||
<Routes4091/>
|
||||
</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>
|
||||
</ul>
|
||||
</nav>
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
pub mod app;
|
||||
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);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#[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
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
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>
|
||||
<ul>
|
||||
<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>
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
@@ -38,6 +38,7 @@ strum = { version = "0.27.1", features = ["strum_macros", "derive"] }
|
||||
notify = { version = "8.0", optional = true }
|
||||
pin-project-lite = "0.2.14"
|
||||
dashmap = { version = "6.0", optional = true }
|
||||
once_cell = { version = "1.19", optional = true }
|
||||
async-broadcast = { version = "0.7.1", optional = true }
|
||||
bytecheck = "0.8.0"
|
||||
rkyv = { version = "0.8.8" }
|
||||
@@ -53,6 +54,7 @@ ssr = [
|
||||
"dep:leptos_axum",
|
||||
"dep:notify",
|
||||
"dep:dashmap",
|
||||
"dep:once_cell",
|
||||
"dep:async-broadcast",
|
||||
]
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ pub fn FileUploadWithProgress() -> impl IntoView {
|
||||
use async_broadcast::{broadcast, Receiver, Sender};
|
||||
use dashmap::DashMap;
|
||||
use futures::Stream;
|
||||
use std::sync::LazyLock;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
struct File {
|
||||
total: usize,
|
||||
@@ -432,8 +432,7 @@ pub fn FileUploadWithProgress() -> impl IntoView {
|
||||
rx: Receiver<usize>,
|
||||
}
|
||||
|
||||
static FILES: LazyLock<DashMap<String, File>> =
|
||||
LazyLock::new(DashMap::new);
|
||||
static FILES: Lazy<DashMap<String, File>> = Lazy::new(DashMap::new);
|
||||
|
||||
pub async fn add_chunk(filename: &str, len: usize) {
|
||||
println!("[{filename}]\tadding {len}");
|
||||
|
||||
@@ -16,6 +16,7 @@ futures = { workspace = true, default-features = true }
|
||||
serde = { features = ["derive"] , workspace = true, default-features = true }
|
||||
wasm-bindgen = { workspace = true, optional = true , default-features = true }
|
||||
js-sys = { optional = true , workspace = true, default-features = true }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
pin-project-lite = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -7,12 +7,10 @@ use super::{SerializedDataId, SharedContext};
|
||||
use crate::{PinnedFuture, PinnedStream};
|
||||
use core::fmt::Debug;
|
||||
use js_sys::Array;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
fmt::Display,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
LazyLock,
|
||||
},
|
||||
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
};
|
||||
use throw_error::{Error, ErrorId};
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
|
||||
@@ -81,8 +79,8 @@ pub struct HydrateSharedContext {
|
||||
id: AtomicUsize,
|
||||
is_hydrating: AtomicBool,
|
||||
during_hydration: AtomicBool,
|
||||
errors: LazyLock<Vec<(SerializedDataId, ErrorId, Error)>>,
|
||||
incomplete: LazyLock<Vec<SerializedDataId>>,
|
||||
errors: Lazy<Vec<(SerializedDataId, ErrorId, Error)>>,
|
||||
incomplete: Lazy<Vec<SerializedDataId>>,
|
||||
}
|
||||
|
||||
impl HydrateSharedContext {
|
||||
@@ -92,8 +90,8 @@ impl HydrateSharedContext {
|
||||
id: AtomicUsize::new(0),
|
||||
is_hydrating: AtomicBool::new(true),
|
||||
during_hydration: AtomicBool::new(true),
|
||||
errors: LazyLock::new(serialized_errors),
|
||||
incomplete: LazyLock::new(incomplete_chunks),
|
||||
errors: Lazy::new(serialized_errors),
|
||||
incomplete: Lazy::new(incomplete_chunks),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,8 +104,8 @@ impl HydrateSharedContext {
|
||||
id: AtomicUsize::new(0),
|
||||
is_hydrating: AtomicBool::new(false),
|
||||
during_hydration: AtomicBool::new(true),
|
||||
errors: LazyLock::new(serialized_errors),
|
||||
incomplete: LazyLock::new(incomplete_chunks),
|
||||
errors: Lazy::new(serialized_errors),
|
||||
incomplete: Lazy::new(incomplete_chunks),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ 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 }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
||||
@@ -38,6 +38,7 @@ use leptos_router::{
|
||||
static_routes::{RegenerationFn, ResolvedStaticPath},
|
||||
ExpandOptionals, Method, PathSegment, RouteList, RouteListing, SsrMode,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::RwLock;
|
||||
use send_wrapper::SendWrapper;
|
||||
use server_fn::{
|
||||
@@ -50,7 +51,7 @@ use std::{
|
||||
future::Future,
|
||||
ops::{Deref, DerefMut},
|
||||
path::Path,
|
||||
sync::{Arc, LazyLock},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// This struct lets you define headers and override the status of the Response from an Element or a Server Function
|
||||
@@ -1209,8 +1210,8 @@ impl StaticRouteGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
static STATIC_HEADERS: LazyLock<DashMap<String, ResponseOptions>> =
|
||||
LazyLock::new(DashMap::new);
|
||||
static STATIC_HEADERS: Lazy<DashMap<String, ResponseOptions>> =
|
||||
Lazy::new(DashMap::new);
|
||||
|
||||
fn was_404(owner: &Owner) -> bool {
|
||||
let resp = owner.with(|| expect_context::<ResponseOptions>());
|
||||
|
||||
@@ -23,6 +23,7 @@ leptos_meta = { workspace = true, features = ["ssr", "nonce"] }
|
||||
leptos_router = { workspace = true, features = ["ssr"] }
|
||||
leptos_integration_utils = { workspace = true }
|
||||
tachys = { workspace = true }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
parking_lot = { workspace = true, default-features = true }
|
||||
tokio = { default-features = false , workspace = true }
|
||||
tower = { features = ["util"] , workspace = true, default-features = true }
|
||||
|
||||
@@ -69,12 +69,12 @@ use leptos_router::{
|
||||
static_routes::RegenerationFn, ExpandOptionals, PathSegment, RouteList,
|
||||
RouteListing, SsrMode,
|
||||
};
|
||||
#[cfg(feature = "default")]
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::RwLock;
|
||||
use server_fn::{error::ServerFnErrorErr, redirect::REDIRECT_HEADER};
|
||||
#[cfg(feature = "default")]
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "default")]
|
||||
use std::sync::LazyLock;
|
||||
use std::{collections::HashSet, fmt::Debug, io, pin::Pin, sync::Arc};
|
||||
#[cfg(feature = "default")]
|
||||
use tower::util::ServiceExt;
|
||||
@@ -1522,8 +1522,8 @@ impl StaticRouteGenerator {
|
||||
}
|
||||
|
||||
#[cfg(feature = "default")]
|
||||
static STATIC_HEADERS: LazyLock<DashMap<String, ResponseOptions>> =
|
||||
LazyLock::new(DashMap::new);
|
||||
static STATIC_HEADERS: Lazy<DashMap<String, ResponseOptions>> =
|
||||
Lazy::new(DashMap::new);
|
||||
|
||||
#[cfg(feature = "default")]
|
||||
fn was_404(owner: &Owner) -> bool {
|
||||
|
||||
@@ -111,39 +111,28 @@ where
|
||||
|
||||
let on_submit = {
|
||||
move |ev: SubmitEvent| {
|
||||
// request_animation_frame here schedules this event handler to run slightly later
|
||||
// this means that this `submit` handler will run *after* any other `submit` handlers
|
||||
// that have been added by the user. this is useful because it means that the user can
|
||||
// add an `on:submit` handler and call `ev.prevent_default()` to prevent the form submission
|
||||
//
|
||||
// without this delay, this handler will always run before the user's handler (which was added
|
||||
// later), which means the user can't prevent the form submission in the same way
|
||||
//
|
||||
// see https://github.com/leptos-rs/leptos/issues/3872
|
||||
request_animation_frame(move || {
|
||||
if ev.default_prevented() {
|
||||
return;
|
||||
}
|
||||
if ev.default_prevented() {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.prevent_default();
|
||||
ev.prevent_default();
|
||||
|
||||
match ServFn::from_event(&ev) {
|
||||
Ok(new_input) => {
|
||||
action.dispatch(new_input);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::logging::error!(
|
||||
"Error converting form field into server function \
|
||||
arguments: {err:?}"
|
||||
);
|
||||
value.set(Some(Err(ServerFnErrorErr::Serialization(
|
||||
err.to_string(),
|
||||
)
|
||||
.into_app_error())));
|
||||
version.update(|n| *n += 1);
|
||||
}
|
||||
match ServFn::from_event(&ev) {
|
||||
Ok(new_input) => {
|
||||
action.dispatch(new_input);
|
||||
}
|
||||
});
|
||||
Err(err) => {
|
||||
crate::logging::error!(
|
||||
"Error converting form field into server function \
|
||||
arguments: {err:?}"
|
||||
);
|
||||
value.set(Some(Err(ServerFnErrorErr::Serialization(
|
||||
err.to_string(),
|
||||
)
|
||||
.into_app_error())));
|
||||
version.update(|n| *n += 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -112,9 +112,9 @@ fn is_inert_element(orig_node: &Node<impl CustomNode>) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// also doesn't work if the top-level element is a MathML element
|
||||
// also doesn't work if the top-level element is an SVG/MathML element
|
||||
let el_name = el.name().to_string();
|
||||
if is_math_ml_element(&el_name) {
|
||||
if is_svg_element(&el_name) || is_math_ml_element(&el_name) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -300,7 +300,7 @@ fn inert_element_to_tokens(
|
||||
node: &Node<impl CustomNode>,
|
||||
escape_text: bool,
|
||||
global_class: Option<&TokenTree>,
|
||||
) -> TokenStream {
|
||||
) -> Option<TokenStream> {
|
||||
let mut html = InertElementBuilder::new(global_class);
|
||||
let mut nodes = VecDeque::from([Item::Node(node, escape_text)]);
|
||||
|
||||
@@ -396,114 +396,9 @@ fn inert_element_to_tokens(
|
||||
|
||||
html.finish();
|
||||
|
||||
quote! {
|
||||
Some(quote! {
|
||||
::leptos::tachys::html::InertElement::new(#html)
|
||||
}
|
||||
}
|
||||
|
||||
fn inert_svg_element_to_tokens(
|
||||
node: &Node<impl CustomNode>,
|
||||
escape_text: bool,
|
||||
global_class: Option<&TokenTree>,
|
||||
) -> TokenStream {
|
||||
let mut html = InertElementBuilder::new(global_class);
|
||||
let mut nodes = VecDeque::from([Item::Node(node, escape_text)]);
|
||||
|
||||
while let Some(current) = nodes.pop_front() {
|
||||
match current {
|
||||
Item::ClosingTag(tag) => {
|
||||
// closing tag
|
||||
html.push_str("</");
|
||||
html.push_str(&tag);
|
||||
html.push('>');
|
||||
}
|
||||
Item::Node(current, escape) => {
|
||||
match current {
|
||||
Node::RawText(raw) => {
|
||||
let text = raw.to_string_best();
|
||||
let text = if escape {
|
||||
html_escape::encode_text(&text)
|
||||
} else {
|
||||
text.into()
|
||||
};
|
||||
html.push_str(&text);
|
||||
}
|
||||
Node::Text(text) => {
|
||||
let text = text.value_string();
|
||||
let text = if escape {
|
||||
html_escape::encode_text(&text)
|
||||
} else {
|
||||
text.into()
|
||||
};
|
||||
html.push_str(&text);
|
||||
}
|
||||
Node::Element(node) => {
|
||||
let self_closing = is_self_closing(node);
|
||||
let el_name = node.name().to_string();
|
||||
let escape = el_name != "script"
|
||||
&& el_name != "style"
|
||||
&& el_name != "textarea";
|
||||
|
||||
// opening tag
|
||||
html.push('<');
|
||||
html.push_str(&el_name);
|
||||
|
||||
for attr in node.attributes() {
|
||||
if let NodeAttribute::Attribute(attr) = attr {
|
||||
let attr_name = attr.key.to_string();
|
||||
// trim r# from raw identifiers like r#as
|
||||
let attr_name =
|
||||
attr_name.trim_start_matches("r#");
|
||||
if attr_name != "class" {
|
||||
html.push(' ');
|
||||
html.push_str(attr_name);
|
||||
}
|
||||
|
||||
if let Some(value) =
|
||||
attr.possible_value.to_value()
|
||||
{
|
||||
if let KVAttributeValue::Expr(Expr::Lit(
|
||||
lit,
|
||||
)) = &value.value
|
||||
{
|
||||
if let Lit::Str(txt) = &lit.lit {
|
||||
let value = txt.value();
|
||||
let value = html_escape::encode_double_quoted_attribute(&value);
|
||||
if attr_name == "class" {
|
||||
html.push_class(&value);
|
||||
} else {
|
||||
html.push_str("=\"");
|
||||
html.push_str(&value);
|
||||
html.push('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
html.push('>');
|
||||
|
||||
// render all children
|
||||
if !self_closing {
|
||||
nodes.push_front(Item::ClosingTag(el_name));
|
||||
let children = node.children.iter().rev();
|
||||
for child in children {
|
||||
nodes.push_front(Item::Node(child, escape));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html.finish();
|
||||
|
||||
quote! {
|
||||
::leptos::tachys::svg::InertElement::new(#html)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn element_children_to_tokens(
|
||||
@@ -702,17 +597,7 @@ fn node_to_tokens(
|
||||
let escape = el_name != "script"
|
||||
&& el_name != "style"
|
||||
&& el_name != "textarea";
|
||||
|
||||
let el_name = el_node.name().to_string();
|
||||
if is_svg_element(&el_name) {
|
||||
Some(inert_svg_element_to_tokens(
|
||||
node,
|
||||
escape,
|
||||
global_class,
|
||||
))
|
||||
} else {
|
||||
Some(inert_element_to_tokens(node, escape, global_class))
|
||||
}
|
||||
inert_element_to_tokens(node, escape, global_class)
|
||||
} else {
|
||||
element_to_tokens(
|
||||
el_node,
|
||||
|
||||
@@ -20,7 +20,7 @@ use reactive_graph::{
|
||||
};
|
||||
use std::{
|
||||
future::{pending, Future, IntoFuture},
|
||||
ops::{Deref, DerefMut},
|
||||
ops::DerefMut,
|
||||
panic::Location,
|
||||
};
|
||||
|
||||
@@ -43,14 +43,6 @@ impl<T> Clone for ArcLocalResource<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for ArcLocalResource<T> {
|
||||
type Target = ArcAsyncDerived<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ArcLocalResource<T> {
|
||||
/// Creates the resource.
|
||||
///
|
||||
@@ -277,14 +269,6 @@ pub struct LocalResource<T> {
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<T> Deref for LocalResource<T> {
|
||||
type Target = AsyncDerived<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for LocalResource<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
|
||||
@@ -10,6 +10,7 @@ edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { workspace = true }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
or_poisoned = { workspace = true }
|
||||
indexmap = { workspace = true, default-features = true }
|
||||
send_wrapper = { workspace = true, default-features = true }
|
||||
|
||||
@@ -63,12 +63,13 @@ use leptos::{
|
||||
},
|
||||
IntoView,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, LazyLock,
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
use wasm_bindgen::JsCast;
|
||||
@@ -100,7 +101,7 @@ pub struct MetaContext {
|
||||
/// Metadata associated with the `<title>` element.
|
||||
pub(crate) title: TitleContext,
|
||||
/// The hydration cursor for the location in the `<head>` for arbitrary tags will be rendered.
|
||||
pub(crate) cursor: Arc<LazyLock<SendWrapper<Cursor>>>,
|
||||
pub(crate) cursor: Arc<Lazy<SendWrapper<Cursor>>>,
|
||||
}
|
||||
|
||||
impl MetaContext {
|
||||
@@ -142,7 +143,7 @@ impl Default for MetaContext {
|
||||
))
|
||||
};
|
||||
|
||||
let cursor = Arc::new(LazyLock::new(build_cursor));
|
||||
let cursor = Arc::new(Lazy::new(build_cursor));
|
||||
Self {
|
||||
title: Default::default(),
|
||||
cursor,
|
||||
|
||||
@@ -21,6 +21,7 @@ fake = "2.9"
|
||||
tokio-tungstenite = "0.23.1"
|
||||
futures-util = "0.3.30"
|
||||
uuid = { version = "1.10", features = ["serde"] }
|
||||
once_cell = "1.19"
|
||||
futures = "0.3.30"
|
||||
|
||||
[[test]]
|
||||
@@ -32,5 +33,6 @@ harness = false # Allow Cucumber to print output instead of libtest
|
||||
ssr = []
|
||||
|
||||
[dependencies]
|
||||
once_cell = "1.19.0"
|
||||
regex = "1.10.6"
|
||||
serde.workspace = true
|
||||
|
||||
@@ -18,14 +18,14 @@ use chromiumoxide::{
|
||||
use cucumber::World;
|
||||
use futures::channel::mpsc::Sender;
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::sync::LazyLock;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_tungstenite::connect_async;
|
||||
use uuid::Uuid;
|
||||
static EMAIL_ID_MAP: LazyLock<RwLock<HashMap<String, String>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
static EMAIL_ID_MAP: Lazy<RwLock<HashMap<String, String>>> =
|
||||
Lazy::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RequestPair {
|
||||
@@ -93,7 +93,7 @@ impl RequestPair {
|
||||
async fn main() -> Result<()> {
|
||||
// create a thread and store a
|
||||
// tokio-tungstenite client that connectsto http://127.0.0.1:1080/ws
|
||||
// and then stores the recieved messages in a std::sync::LazyLock<RwLock<Vec<MailCrabMsg>>>
|
||||
// and then stores the recieved messages in a once_cell::Lazy<RwLock<Vec<MailCrabMsg>>>
|
||||
// or a custom struct that matches the body or has specific impls for verify codes, links etc.
|
||||
let _ = tokio::spawn(async move {
|
||||
let (mut socket, _) = connect_async(
|
||||
@@ -152,7 +152,7 @@ async fn main() -> Result<()> {
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
while let Some(event) = log_events.next().await {
|
||||
if let Some(EventEntryAdded { entry }) =
|
||||
if let Some(EventEntryAdded { entry }) =
|
||||
Arc::<EventEntryAdded>::into_inner(event) {
|
||||
console_logs.write().await.push(format!(" {entry:#?} "));
|
||||
} else {
|
||||
@@ -171,7 +171,7 @@ async fn main() -> Result<()> {
|
||||
} else {
|
||||
tracing::error!("tried to into inner but none")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -208,7 +208,7 @@ async fn main() -> Result<()> {
|
||||
thing.cookies_before_request = cookies;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
CookieEnum::AfterResp(req_id) => {
|
||||
let cookies = page
|
||||
@@ -293,8 +293,8 @@ async fn main() -> Result<()> {
|
||||
} else {
|
||||
tracing::error!(" uhh err here")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
// We don't need to join on our join handles, they will run detached and clean up whenever.
|
||||
|
||||
@@ -167,6 +167,7 @@ impl Owner {
|
||||
.map(|parent| parent.read().or_poisoned().arena.clone())
|
||||
.unwrap_or_default(),
|
||||
paused: false,
|
||||
joined_owners: Vec::new(),
|
||||
})),
|
||||
#[cfg(feature = "hydration")]
|
||||
shared_context,
|
||||
@@ -201,6 +202,7 @@ impl Owner {
|
||||
#[cfg(feature = "sandboxed-arenas")]
|
||||
arena: Default::default(),
|
||||
paused: false,
|
||||
joined_owners: Vec::new(),
|
||||
})),
|
||||
#[cfg(feature = "hydration")]
|
||||
shared_context,
|
||||
@@ -226,6 +228,7 @@ impl Owner {
|
||||
#[cfg(feature = "sandboxed-arenas")]
|
||||
arena,
|
||||
paused,
|
||||
joined_owners: Vec::new(),
|
||||
})),
|
||||
#[cfg(feature = "hydration")]
|
||||
shared_context: self.shared_context.clone(),
|
||||
@@ -461,6 +464,7 @@ pub(crate) struct OwnerInner {
|
||||
#[cfg(feature = "sandboxed-arenas")]
|
||||
arena: Arc<RwLock<ArenaMap>>,
|
||||
paused: bool,
|
||||
joined_owners: Vec<WeakOwner>,
|
||||
}
|
||||
|
||||
impl Debug for OwnerInner {
|
||||
|
||||
@@ -6,6 +6,15 @@ use std::{
|
||||
};
|
||||
|
||||
impl Owner {
|
||||
#[doc(hidden)]
|
||||
pub fn join_contexts(&self, other: &Owner) {
|
||||
self.inner
|
||||
.write()
|
||||
.or_poisoned()
|
||||
.joined_owners
|
||||
.push(other.downgrade());
|
||||
}
|
||||
|
||||
fn provide_context<T: Send + Sync + 'static>(&self, value: T) {
|
||||
self.inner
|
||||
.write()
|
||||
@@ -25,18 +34,27 @@ impl Owner {
|
||||
if let Some(context) = contexts.remove(&ty) {
|
||||
context.downcast::<T>().ok().map(|n| *n)
|
||||
} else {
|
||||
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
|
||||
while let Some(ref this_parent) = parent.clone() {
|
||||
let mut this_parent = this_parent.write().or_poisoned();
|
||||
let contexts = &mut this_parent.contexts;
|
||||
let value = contexts.remove(&ty);
|
||||
let downcast =
|
||||
value.and_then(|context| context.downcast::<T>().ok());
|
||||
if let Some(value) = downcast {
|
||||
return Some(*value);
|
||||
} else {
|
||||
parent =
|
||||
this_parent.parent.as_ref().and_then(|p| p.upgrade());
|
||||
let parent = inner.parent.as_ref().and_then(|p| p.upgrade());
|
||||
let joined = inner
|
||||
.joined_owners
|
||||
.iter()
|
||||
.flat_map(|owner| owner.upgrade().map(|owner| owner.inner));
|
||||
for parent in parent.into_iter().chain(joined) {
|
||||
let mut parent = Some(parent);
|
||||
while let Some(ref this_parent) = parent.clone() {
|
||||
let mut this_parent = this_parent.write().or_poisoned();
|
||||
let contexts = &mut this_parent.contexts;
|
||||
let value = contexts.remove(&ty);
|
||||
let downcast =
|
||||
value.and_then(|context| context.downcast::<T>().ok());
|
||||
if let Some(value) = downcast {
|
||||
return Some(*value);
|
||||
} else {
|
||||
parent = this_parent
|
||||
.parent
|
||||
.as_ref()
|
||||
.and_then(|p| p.upgrade());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
@@ -53,21 +71,29 @@ impl Owner {
|
||||
let reference = if let Some(context) = contexts.get(&ty) {
|
||||
context.downcast_ref::<T>()
|
||||
} else {
|
||||
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
|
||||
while let Some(ref this_parent) = parent.clone() {
|
||||
let this_parent = this_parent.read().or_poisoned();
|
||||
let contexts = &this_parent.contexts;
|
||||
let value = contexts.get(&ty);
|
||||
let downcast =
|
||||
value.and_then(|context| context.downcast_ref::<T>());
|
||||
if let Some(value) = downcast {
|
||||
return Some(cb(value));
|
||||
} else {
|
||||
parent =
|
||||
this_parent.parent.as_ref().and_then(|p| p.upgrade());
|
||||
let parent = inner.parent.as_ref().and_then(|p| p.upgrade());
|
||||
let joined = inner
|
||||
.joined_owners
|
||||
.iter()
|
||||
.flat_map(|owner| owner.upgrade().map(|owner| owner.inner));
|
||||
for parent in parent.into_iter().chain(joined) {
|
||||
let mut parent = Some(parent);
|
||||
while let Some(ref this_parent) = parent.clone() {
|
||||
let this_parent = this_parent.read().or_poisoned();
|
||||
let contexts = &this_parent.contexts;
|
||||
let value = contexts.get(&ty);
|
||||
let downcast =
|
||||
value.and_then(|context| context.downcast_ref::<T>());
|
||||
if let Some(value) = downcast {
|
||||
return Some(cb(value));
|
||||
} else {
|
||||
parent = this_parent
|
||||
.parent
|
||||
.as_ref()
|
||||
.and_then(|p| p.upgrade());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
reference.map(cb)
|
||||
@@ -83,18 +109,27 @@ impl Owner {
|
||||
let reference = if let Some(context) = contexts.get_mut(&ty) {
|
||||
context.downcast_mut::<T>()
|
||||
} else {
|
||||
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
|
||||
while let Some(ref this_parent) = parent.clone() {
|
||||
let mut this_parent = this_parent.write().or_poisoned();
|
||||
let contexts = &mut this_parent.contexts;
|
||||
let value = contexts.get_mut(&ty);
|
||||
let downcast =
|
||||
value.and_then(|context| context.downcast_mut::<T>());
|
||||
if let Some(value) = downcast {
|
||||
return Some(cb(value));
|
||||
} else {
|
||||
parent =
|
||||
this_parent.parent.as_ref().and_then(|p| p.upgrade());
|
||||
let parent = inner.parent.as_ref().and_then(|p| p.upgrade());
|
||||
let joined = inner
|
||||
.joined_owners
|
||||
.iter()
|
||||
.flat_map(|owner| owner.upgrade().map(|owner| owner.inner));
|
||||
for parent in parent.into_iter().chain(joined) {
|
||||
let mut parent = Some(parent);
|
||||
while let Some(ref this_parent) = parent.clone() {
|
||||
let mut this_parent = this_parent.write().or_poisoned();
|
||||
let contexts = &mut this_parent.contexts;
|
||||
let value = contexts.get_mut(&ty);
|
||||
let downcast =
|
||||
value.and_then(|context| context.downcast_mut::<T>());
|
||||
if let Some(value) = downcast {
|
||||
return Some(cb(value));
|
||||
} else {
|
||||
parent = this_parent
|
||||
.parent
|
||||
.as_ref()
|
||||
.and_then(|p| p.upgrade());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -22,6 +22,7 @@ url = { workspace = true, default-features = true }
|
||||
js-sys = { workspace = true, default-features = true }
|
||||
wasm-bindgen = { workspace = true , default-features = true }
|
||||
tracing = { optional = true , workspace = true, default-features = true }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
send_wrapper = { workspace = true, default-features = true }
|
||||
thiserror = { workspace = true , default-features = true }
|
||||
percent-encoding = { optional = true , workspace = true, default-features = true }
|
||||
|
||||
@@ -144,12 +144,16 @@ impl RouterContext {
|
||||
resolve_path("", path, None)
|
||||
};
|
||||
|
||||
let mut url = match BrowserUrl::parse(&resolved_to) {
|
||||
Ok(url) => url,
|
||||
Err(e) => {
|
||||
let mut url = match resolved_to.map(|to| BrowserUrl::parse(&to)) {
|
||||
Some(Ok(url)) => url,
|
||||
Some(Err(e)) => {
|
||||
leptos::logging::error!("Error parsing URL: {e:?}");
|
||||
return;
|
||||
}
|
||||
None => {
|
||||
leptos::logging::error!("Error resolving relative URL.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let query_mutations =
|
||||
mem::take(&mut *self.query_mutations.write_value());
|
||||
@@ -199,7 +203,7 @@ impl RouterContext {
|
||||
&'a self,
|
||||
path: &'a str,
|
||||
from: Option<&'a str>,
|
||||
) -> Cow<'a, str> {
|
||||
) -> Option<Cow<'a, str>> {
|
||||
let base = self.base.as_deref().unwrap_or_default();
|
||||
resolve_path(base, path, from)
|
||||
}
|
||||
@@ -576,11 +580,18 @@ pub fn Redirect<P>(
|
||||
|
||||
// redirect on the server
|
||||
if let Some(redirect_fn) = use_context::<ServerRedirectFunction>() {
|
||||
(redirect_fn.f)(&resolve_path(
|
||||
"",
|
||||
&path,
|
||||
Some(&use_matched().get_untracked()),
|
||||
));
|
||||
match resolve_path("", &path, Some(&use_matched().get_untracked())) {
|
||||
Some(path) => (redirect_fn.f)(&path),
|
||||
None => {
|
||||
if cfg!(feature = "ssr") {
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::warn!("Error resolving relative URL.");
|
||||
|
||||
#[cfg(not(feature = "tracing"))]
|
||||
eprintln!("Error resolving relative URL.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// redirect on the client
|
||||
else {
|
||||
|
||||
@@ -87,7 +87,7 @@ where
|
||||
fn inner(
|
||||
has_router: bool,
|
||||
method: Option<&'static str>,
|
||||
action: ArcMemo<String>,
|
||||
action: ArcMemo<Option<String>>,
|
||||
enctype: Option<String>,
|
||||
version: Option<RwSignal<usize>>,
|
||||
error: Option<RwSignal<Option<Box<dyn Error + Send + Sync>>>>,
|
||||
@@ -311,7 +311,7 @@ where
|
||||
let action = if has_router {
|
||||
use_resolved_path(move || action.to_href()())
|
||||
} else {
|
||||
ArcMemo::new(move |_| action.to_href()())
|
||||
ArcMemo::new(move |_| Some(action.to_href()()))
|
||||
};
|
||||
inner(
|
||||
has_router,
|
||||
|
||||
@@ -240,7 +240,7 @@ pub(crate) struct Matched(pub ArcMemo<String>);
|
||||
#[track_caller]
|
||||
pub(crate) fn use_resolved_path(
|
||||
path: impl Fn() -> String + Send + Sync + 'static,
|
||||
) -> ArcMemo<String> {
|
||||
) -> ArcMemo<Option<String>> {
|
||||
let router = use_context::<RouterContext>()
|
||||
.expect("called use_resolved_path outside a <Router>");
|
||||
// TODO make this work with flat routes too?
|
||||
@@ -248,14 +248,14 @@ pub(crate) fn use_resolved_path(
|
||||
ArcMemo::new(move |_| {
|
||||
let path = path();
|
||||
if path.starts_with('/') {
|
||||
path
|
||||
Some(path)
|
||||
} else {
|
||||
router
|
||||
.resolve_path(
|
||||
&path,
|
||||
matched.as_ref().map(|n| n.get()).as_deref(),
|
||||
)
|
||||
.to_string()
|
||||
.map(|n| n.to_string())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ where
|
||||
H: ToHref + Send + Sync + 'static,
|
||||
{
|
||||
fn inner(
|
||||
href: ArcMemo<String>,
|
||||
href: ArcMemo<Option<String>>,
|
||||
target: Option<Oco<'static, str>>,
|
||||
exact: bool,
|
||||
children: Children,
|
||||
@@ -133,21 +133,23 @@ where
|
||||
let is_active = {
|
||||
let href = href.clone();
|
||||
move || {
|
||||
let path = normalize_path(&href.read());
|
||||
current_url.with(|loc| {
|
||||
let loc = loc.path();
|
||||
if exact {
|
||||
loc == path
|
||||
} else {
|
||||
is_active_for(&path, loc, strict_trailing_slash)
|
||||
}
|
||||
href.read().as_deref().is_some_and(|to| {
|
||||
let path = to.split(['?', '#']).next().unwrap_or_default();
|
||||
current_url.with(|loc| {
|
||||
let loc = loc.path();
|
||||
if exact {
|
||||
loc == path
|
||||
} else {
|
||||
is_active_for(path, loc, strict_trailing_slash)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
view! {
|
||||
<a
|
||||
href=move || href.get()
|
||||
href=move || href.get().unwrap_or_default()
|
||||
target=target
|
||||
aria-current=move || if is_active() { Some("page") } else { None }
|
||||
data-noscroll=!scroll
|
||||
@@ -189,62 +191,9 @@ fn is_active_for(
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve `".."` segments in the path. Assume path is either empty or starts with a `'/'``.
|
||||
fn normalize_path(path: &str) -> String {
|
||||
// Return only on the only condition where leading slash
|
||||
// is allowed to be missing.
|
||||
if path.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let mut del = 0;
|
||||
let mut it = path
|
||||
.split(['?', '#'])
|
||||
.next()
|
||||
.unwrap_or_default()
|
||||
.split(['/'])
|
||||
.rev()
|
||||
.peekable();
|
||||
|
||||
let init = if it.peek() == Some(&"..") {
|
||||
String::from("/")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let mut path = it
|
||||
.filter(|v| {
|
||||
if *v == ".." {
|
||||
del += 1;
|
||||
false
|
||||
} else if *v == "." {
|
||||
false
|
||||
} else if del > 0 {
|
||||
del -= 1;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
// We cannot reverse before the fold again bc the filter
|
||||
// would be forwards again.
|
||||
.fold(init, |mut p, v| {
|
||||
p.reserve(v.len() + 1);
|
||||
p.insert(0, '/');
|
||||
p.insert_str(0, v);
|
||||
p
|
||||
});
|
||||
path.truncate(path.len().saturating_sub(1));
|
||||
|
||||
// Path starts with '/' giving it an extra empty segment after the split
|
||||
// Which should not be removed.
|
||||
if !path.starts_with('/') {
|
||||
path.insert(0, '/');
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{is_active_for, normalize_path};
|
||||
use super::is_active_for;
|
||||
|
||||
#[test]
|
||||
fn is_active_for_matched() {
|
||||
@@ -463,37 +412,4 @@ mod tests {
|
||||
// assert!(!is_same_level("/some/", "/some/level/"))
|
||||
// assert!(!is_same_level("/some/", "/some/level/deeper"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_path_test() {
|
||||
// Make sure it doesn't touch already normalized urls.
|
||||
assert!(normalize_path("") == "".to_string());
|
||||
assert!(normalize_path("/") == "/".to_string());
|
||||
assert!(normalize_path("/some") == "/some".to_string());
|
||||
assert!(normalize_path("/some/") == "/some/".to_string());
|
||||
|
||||
// Correctly removes ".." segments.
|
||||
assert!(normalize_path("/some/../another") == "/another".to_string());
|
||||
assert!(
|
||||
normalize_path("/one/two/../three/../../four")
|
||||
== "/four".to_string()
|
||||
);
|
||||
|
||||
// Correctly sets trailing slash if last segement is "..".
|
||||
assert!(normalize_path("/one/two/..") == "/one/".to_string());
|
||||
assert!(normalize_path("/one/two/../") == "/one/".to_string());
|
||||
|
||||
// Level outside of the url.
|
||||
assert!(normalize_path("/..") == "/".to_string());
|
||||
assert!(normalize_path("/../") == "/".to_string());
|
||||
|
||||
// Going into negative levels and coming back into the positives.
|
||||
assert!(
|
||||
normalize_path("/one/../../two/three") == "/two/three".to_string()
|
||||
);
|
||||
assert!(
|
||||
normalize_path("/one/../../two/three/")
|
||||
== "/two/three/".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ pub fn resolve_path<'a>(
|
||||
base: &'a str,
|
||||
path: &'a str,
|
||||
from: Option<&'a str>,
|
||||
) -> Cow<'a, str> {
|
||||
) -> Option<Cow<'a, str>> {
|
||||
if has_scheme(path) {
|
||||
path.into()
|
||||
Some(path.into())
|
||||
} else {
|
||||
let base_path = normalize(base, false);
|
||||
let from_path = from.map(|from| normalize(from, false));
|
||||
@@ -25,7 +25,7 @@ pub fn resolve_path<'a>(
|
||||
let result_empty = result.is_empty();
|
||||
let prefix = if result_empty { "/".into() } else { result };
|
||||
|
||||
prefix + normalize(path, result_empty)
|
||||
Some(prefix + normalize(path, result_empty))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,9 +65,6 @@ where
|
||||
// TODO loading fallback
|
||||
#[allow(clippy::type_complexity)]
|
||||
view: Rc<RefCell<EitherOf3State<(), Fal, AnyView>>>,
|
||||
// held to keep the Owner alive until the router is dropped
|
||||
#[allow(unused)]
|
||||
outer_owner: Owner,
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> Render for NestedRoutesView<Loc, Defs, FalFn>
|
||||
@@ -112,8 +109,7 @@ where
|
||||
&outer_owner,
|
||||
);
|
||||
drop(url);
|
||||
|
||||
EitherOf3::C(top_level_outlet(&outlets, &outer_owner))
|
||||
outer_owner.with(|| EitherOf3::C(Outlet().into_any()))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,7 +130,6 @@ where
|
||||
current_url,
|
||||
outlets,
|
||||
view,
|
||||
outer_owner,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,14 +212,17 @@ where
|
||||
|
||||
// if it was on the fallback, show the view instead
|
||||
if matches!(state.view.borrow().state, EitherOf3::B(_)) {
|
||||
EitherOf3::<(), Fal, AnyView>::C(top_level_outlet(
|
||||
&state.outlets,
|
||||
&self.outer_owner,
|
||||
))
|
||||
.rebuild(&mut *state.view.borrow_mut());
|
||||
self.outer_owner.with(|| {
|
||||
EitherOf3::<(), Fal, AnyView>::C(Outlet().into_any())
|
||||
.rebuild(&mut *state.view.borrow_mut());
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(outlet) = state.outlets.first() {
|
||||
self.outer_owner.with(|| outlet.provide_contexts());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +348,7 @@ where
|
||||
.now_or_never()
|
||||
.expect("async routes not supported in SSR");
|
||||
|
||||
Either::Right(top_level_outlet(&outlets, &outer_owner))
|
||||
outer_owner.with(|| Either::Right(Outlet().into_any()))
|
||||
}
|
||||
};
|
||||
view.to_html_with_buf(
|
||||
@@ -404,7 +402,7 @@ where
|
||||
.now_or_never()
|
||||
.expect("async routes not supported in SSR");
|
||||
|
||||
Either::Right(top_level_outlet(&outlets, &outer_owner))
|
||||
outer_owner.with(|| Either::Right(Outlet().into_any()))
|
||||
}
|
||||
};
|
||||
view.to_html_async_with_buf::<OUT_OF_ORDER>(
|
||||
@@ -456,7 +454,7 @@ where
|
||||
join_all(mem::take(&mut loaders))
|
||||
.now_or_never()
|
||||
.expect("async routes not supported in SSR");
|
||||
EitherOf3::C(top_level_outlet(&outlets, &outer_owner))
|
||||
outer_owner.with(|| EitherOf3::C(Outlet().into_any()))
|
||||
}
|
||||
}
|
||||
.hydrate::<FROM_SERVER>(cursor, position),
|
||||
@@ -467,7 +465,6 @@ where
|
||||
current_url,
|
||||
outlets,
|
||||
view,
|
||||
outer_owner,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,12 +484,8 @@ pub(crate) struct RouteContext {
|
||||
pub matched: ArcRwSignal<String>,
|
||||
base: Option<Oco<'static, str>>,
|
||||
view_fn: Arc<Mutex<OutletViewFn>>,
|
||||
child: ChildRoute,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ChildRoute(Arc<Mutex<Option<RouteContext>>>);
|
||||
|
||||
impl Debug for RouteContext {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RouteContext")
|
||||
@@ -507,6 +500,12 @@ impl Debug for RouteContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl RouteContext {
|
||||
fn provide_contexts(&self) {
|
||||
provide_context(self.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RouteContext {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@@ -518,7 +517,6 @@ impl Clone for RouteContext {
|
||||
matched: self.matched.clone(),
|
||||
base: self.base.clone(),
|
||||
view_fn: Arc::clone(&self.view_fn),
|
||||
child: self.child.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,68 +628,60 @@ where
|
||||
Suspend::new(Box::pin(async { ().into_any() }))
|
||||
}))),
|
||||
base: base.clone(),
|
||||
child: ChildRoute(Arc::new(Mutex::new(None))),
|
||||
};
|
||||
if !outlets.is_empty() {
|
||||
let prev_index = outlets.len().saturating_sub(1);
|
||||
*outlets[prev_index].child.0.lock().or_poisoned() =
|
||||
Some(outlet.clone());
|
||||
}
|
||||
outlets.push(outlet.clone());
|
||||
|
||||
// send the initial view through the channel, and recurse through the children
|
||||
let (view, child) = self.into_view_and_child();
|
||||
|
||||
loaders.push(Box::pin(ScopedFuture::new({
|
||||
let url = outlet.url.clone();
|
||||
let matched = Matched(matched_including_parents);
|
||||
let view_fn = Arc::clone(&outlet.view_fn);
|
||||
let outlet = outlet.clone();
|
||||
let params = params_including_parents.clone();
|
||||
let url = url.clone();
|
||||
let matched = matched.clone();
|
||||
async move {
|
||||
view.preload().await;
|
||||
let child = outlet.child.clone();
|
||||
*view_fn.lock().or_poisoned() =
|
||||
Box::new(move |owner_where_used| {
|
||||
let view = view.clone();
|
||||
let child = child.clone();
|
||||
let params = params.clone();
|
||||
let url = url.clone();
|
||||
let matched = matched.clone();
|
||||
owner_where_used.with({
|
||||
let matched = matched.clone();
|
||||
move || {
|
||||
let child = child.clone();
|
||||
Suspend::new(Box::pin(async move {
|
||||
provide_context(child.clone());
|
||||
provide_context(params.clone());
|
||||
provide_context(url.clone());
|
||||
provide_context(matched.clone());
|
||||
let view = SendWrapper::new(
|
||||
ScopedFuture::new(view.choose()),
|
||||
);
|
||||
let view = view.await;
|
||||
let view = MatchedRoute(
|
||||
matched.0.get_untracked(),
|
||||
view,
|
||||
);
|
||||
|
||||
OwnedView::new(view).into_any()
|
||||
})
|
||||
as Pin<
|
||||
Box<
|
||||
dyn Future<Output = AnyView> + Send,
|
||||
>,
|
||||
>)
|
||||
}
|
||||
})
|
||||
});
|
||||
trigger
|
||||
}
|
||||
loaders.push(Box::pin(owner.with(|| {
|
||||
ScopedFuture::new({
|
||||
let owner = outlet.owner.clone();
|
||||
let url = outlet.url.clone();
|
||||
let matched = Matched(matched_including_parents);
|
||||
let view_fn = Arc::clone(&outlet.view_fn);
|
||||
async move {
|
||||
provide_context(params_including_parents);
|
||||
provide_context(url);
|
||||
provide_context(matched.clone());
|
||||
view.preload().await;
|
||||
*view_fn.lock().or_poisoned() =
|
||||
Box::new(move |owner_where_used| {
|
||||
owner.join_contexts(&owner_where_used);
|
||||
let view = view.clone();
|
||||
owner.with({
|
||||
let matched = matched.clone();
|
||||
move || {
|
||||
Suspend::new(Box::pin(async move {
|
||||
let view = SendWrapper::new(
|
||||
ScopedFuture::new(view.choose()),
|
||||
);
|
||||
let view = view.await;
|
||||
let view = MatchedRoute(
|
||||
matched.0.get_untracked(),
|
||||
view,
|
||||
);
|
||||
OwnedView::new(view).into_any()
|
||||
})
|
||||
as Pin<
|
||||
Box<
|
||||
dyn Future<Output = AnyView>
|
||||
+ Send,
|
||||
>,
|
||||
>)
|
||||
}
|
||||
})
|
||||
});
|
||||
trigger
|
||||
}
|
||||
})
|
||||
})));
|
||||
|
||||
// and share the outlet with the parent via context
|
||||
// we share it with the *parent* because the <Outlet/> is rendered in or below the parent
|
||||
// wherever it appears, <Outlet/> will look for the closest RouteContext
|
||||
parent.with(|| outlet.provide_contexts());
|
||||
|
||||
// recursively continue building the tree
|
||||
// this is important because to build the view, we need access to the outlet
|
||||
// and the outlet will be returned from building this child
|
||||
@@ -795,71 +785,64 @@ where
|
||||
let (full_tx, full_rx) = oneshot::channel();
|
||||
let full_tx = Mutex::new(Some(full_tx));
|
||||
full_loaders.push(full_rx);
|
||||
let outlet = current.clone();
|
||||
|
||||
// send the new view, with the new owner, through the channel to the Outlet,
|
||||
// and notify the trigger so that the reactive view inside the Outlet tracking
|
||||
// the trigger runs again
|
||||
preloaders.push(Box::pin(ScopedFuture::new({
|
||||
let owner = owner.clone();
|
||||
let trigger = current.trigger.clone();
|
||||
let url = current.url.clone();
|
||||
let matched = Matched(matched_including_parents);
|
||||
let view_fn = Arc::clone(¤t.view_fn);
|
||||
let child = outlet.child.clone();
|
||||
async move {
|
||||
view.preload().await;
|
||||
let child = child.clone();
|
||||
*view_fn.lock().or_poisoned() =
|
||||
Box::new(move |owner_where_used| {
|
||||
let owner = owner.clone();
|
||||
let view = view.clone();
|
||||
let full_tx =
|
||||
full_tx.lock().or_poisoned().take();
|
||||
let old_owner = old_owner.take();
|
||||
let child = child.clone();
|
||||
let params =
|
||||
params_including_parents.clone();
|
||||
let url = url.clone();
|
||||
let matched = matched.clone();
|
||||
Suspend::new(Box::pin(async move {
|
||||
let view = SendWrapper::new(
|
||||
owner_where_used.with(|| {
|
||||
provide_context(child.clone());
|
||||
provide_context(params);
|
||||
provide_context(url);
|
||||
provide_context(matched);
|
||||
ScopedFuture::new(async move {
|
||||
if set_is_routing {
|
||||
AsyncTransition::run(
|
||||
|| view.choose(),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
view.choose().await
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
let view = view.await;
|
||||
if let Some(old_owner) = old_owner {
|
||||
old_owner.cleanup();
|
||||
}
|
||||
preloaders.push(Box::pin(owner.with(|| {
|
||||
ScopedFuture::new({
|
||||
let owner = owner.clone();
|
||||
let trigger = current.trigger.clone();
|
||||
let url = current.url.clone();
|
||||
let matched = Matched(matched_including_parents);
|
||||
let view_fn = Arc::clone(¤t.view_fn);
|
||||
async move {
|
||||
provide_context(params_including_parents);
|
||||
provide_context(url);
|
||||
provide_context(matched);
|
||||
view.preload().await;
|
||||
*view_fn.lock().or_poisoned() =
|
||||
Box::new(move |owner_where_used| {
|
||||
owner.join_contexts(&owner_where_used);
|
||||
let owner = owner.clone();
|
||||
let view = view.clone();
|
||||
let full_tx =
|
||||
full_tx.lock().or_poisoned().take();
|
||||
let old_owner = old_owner.take();
|
||||
Suspend::new(Box::pin(async move {
|
||||
let view = SendWrapper::new(
|
||||
owner.with(|| {
|
||||
ScopedFuture::new(
|
||||
async move {
|
||||
if set_is_routing {
|
||||
AsyncTransition::run(|| view.choose()).await
|
||||
} else {
|
||||
view.choose().await
|
||||
}
|
||||
}
|
||||
)
|
||||
}),
|
||||
);
|
||||
let view = view.await;
|
||||
if let Some(old_owner) = old_owner {
|
||||
old_owner.cleanup();
|
||||
}
|
||||
|
||||
if let Some(tx) = full_tx {
|
||||
_ = tx.send(());
|
||||
}
|
||||
owner.with(|| {
|
||||
OwnedView::new(view).into_any()
|
||||
})
|
||||
}))
|
||||
});
|
||||
if let Some(tx) = full_tx {
|
||||
_ = tx.send(());
|
||||
}
|
||||
owner.with(|| {
|
||||
OwnedView::new(view).into_any()
|
||||
})
|
||||
}))
|
||||
});
|
||||
|
||||
drop(old_params);
|
||||
drop(old_url);
|
||||
drop(old_matched);
|
||||
trigger
|
||||
}
|
||||
drop(old_params);
|
||||
drop(old_url);
|
||||
drop(old_matched);
|
||||
trigger
|
||||
}
|
||||
})
|
||||
})));
|
||||
|
||||
// remove all the items lower in the tree
|
||||
@@ -928,38 +911,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn top_level_outlet(outlets: &[RouteContext], outer_owner: &Owner) -> AnyView {
|
||||
let outlet = outlets.first().unwrap();
|
||||
let child = outlet.child.clone();
|
||||
let view_fn = outlet.view_fn.clone();
|
||||
let trigger = outlet.trigger.clone();
|
||||
let owner = outer_owner.child();
|
||||
outer_owner.clone().with(|| {
|
||||
provide_context(child.clone());
|
||||
(move || {
|
||||
trigger.track();
|
||||
let mut view_fn = view_fn.lock().or_poisoned();
|
||||
view_fn(owner.clone())
|
||||
})
|
||||
.into_any()
|
||||
})
|
||||
}
|
||||
|
||||
/// Displays the child route nested in a parent route, allowing you to control exactly where
|
||||
/// that child route is displayed. Renders nothing if there is no nested child.
|
||||
#[component]
|
||||
pub fn Outlet() -> impl RenderHtml
|
||||
where
|
||||
{
|
||||
let ChildRoute(child) = use_context()
|
||||
.expect("<Outlet/> used without RouteContext being provided.");
|
||||
let owner = Owner::new();
|
||||
let child = child.lock().or_poisoned().clone();
|
||||
child.map(|child| {
|
||||
move || {
|
||||
child.trigger.track();
|
||||
let mut view_fn = child.view_fn.lock().or_poisoned();
|
||||
view_fn(owner.clone())
|
||||
}
|
||||
})
|
||||
move || {
|
||||
let ctx = use_context::<RouteContext>()
|
||||
.expect("<Outlet/> used without RouteContext being provided.");
|
||||
let RouteContext {
|
||||
trigger, view_fn, ..
|
||||
} = ctx;
|
||||
trigger.track();
|
||||
let mut view_fn = view_fn.lock().or_poisoned();
|
||||
view_fn(Owner::current().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ thiserror = { workspace = true, default-features = true }
|
||||
# registration system
|
||||
inventory = { optional = true, workspace = true, default-features = true }
|
||||
dashmap = { workspace = true, default-features = true }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
|
||||
## servers
|
||||
# actix
|
||||
|
||||
@@ -151,6 +151,7 @@ use error::{FromServerFnError, ServerFnErrorErr};
|
||||
use futures::{pin_mut, SinkExt, Stream, StreamExt};
|
||||
use http::Method;
|
||||
use middleware::{BoxedService, Layer, Service};
|
||||
use once_cell::sync::Lazy;
|
||||
use redirect::call_redirect_hook;
|
||||
use request::Req;
|
||||
use response::{ClientRes, Res, TryRes};
|
||||
@@ -168,7 +169,7 @@ use std::{
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
sync::{Arc, LazyLock},
|
||||
sync::Arc,
|
||||
};
|
||||
#[doc(hidden)]
|
||||
pub use xxhash_rust;
|
||||
@@ -861,7 +862,7 @@ pub use inventory;
|
||||
#[macro_export]
|
||||
macro_rules! initialize_server_fn_map {
|
||||
($req:ty, $res:ty) => {
|
||||
std::sync::LazyLock::new(|| {
|
||||
once_cell::sync::Lazy::new(|| {
|
||||
$crate::inventory::iter::<ServerFnTraitObj<$req, $res>>
|
||||
.into_iter()
|
||||
.map(|obj| {
|
||||
@@ -980,7 +981,7 @@ impl<Req, Res> Clone for ServerFnTraitObj<Req, Res> {
|
||||
|
||||
#[allow(unused)] // used by server integrations
|
||||
type LazyServerFnMap<Req, Res> =
|
||||
LazyLock<DashMap<(String, Method), ServerFnTraitObj<Req, Res>>>;
|
||||
Lazy<DashMap<(String, Method), ServerFnTraitObj<Req, Res>>>;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
impl<Req: 'static, Res: 'static> inventory::Collect
|
||||
|
||||
@@ -75,13 +75,6 @@ impl DerefMut for BrowserRequest {
|
||||
#[derive(Debug)]
|
||||
pub struct BrowserFormData(pub(crate) SendWrapper<FormData>);
|
||||
|
||||
impl BrowserFormData {
|
||||
/// Returns the raw `web_sys::FormData` struct.
|
||||
pub fn take(self) -> FormData {
|
||||
self.0.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FormData> for BrowserFormData {
|
||||
fn from(value: FormData) -> Self {
|
||||
Self(SendWrapper::new(value))
|
||||
|
||||
@@ -5,14 +5,14 @@ use crate::{
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures::{Stream, StreamExt};
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::{
|
||||
header::{ACCEPT, CONTENT_TYPE},
|
||||
Body,
|
||||
};
|
||||
pub use reqwest::{multipart::Form, Client, Method, Request, Url};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
pub(crate) static CLIENT: LazyLock<Client> = LazyLock::new(Client::new);
|
||||
pub(crate) static CLIENT: Lazy<Client> = Lazy::new(Client::new);
|
||||
|
||||
impl<E> ClientReq<E> for Request
|
||||
where
|
||||
|
||||
@@ -21,6 +21,7 @@ reactive_stores = { workspace = true, optional = true }
|
||||
slotmap = { optional = true, workspace = true, default-features = true }
|
||||
oco_ref = { workspace = true, optional = true }
|
||||
async-trait = { workspace = true, default-features = true }
|
||||
once_cell = { workspace = true, default-features = true }
|
||||
paste = { workspace = true, default-features = true }
|
||||
erased = { workspace = true, default-features = true }
|
||||
wasm-bindgen = { workspace = true, default-features = true }
|
||||
|
||||
@@ -540,20 +540,11 @@ impl IntoClass for (&'static str, bool) {
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (name, include) = self;
|
||||
let (class_list, prev_include, prev_name) = state;
|
||||
if name == *prev_name {
|
||||
if include != *prev_include {
|
||||
if include {
|
||||
Rndr::add_class(class_list, name);
|
||||
} else {
|
||||
Rndr::remove_class(class_list, name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if *prev_include {
|
||||
Rndr::remove_class(class_list, prev_name);
|
||||
}
|
||||
if include != *prev_include {
|
||||
if include {
|
||||
Rndr::add_class(class_list, name);
|
||||
} else {
|
||||
Rndr::remove_class(class_list, name);
|
||||
}
|
||||
}
|
||||
*prev_include = include;
|
||||
|
||||
@@ -709,7 +709,7 @@ where
|
||||
|
||||
buf.push('<');
|
||||
buf.push_str(E::TAG);
|
||||
<At as ToTemplate>::to_template_attribute(
|
||||
<At as ToTemplate>::to_template(
|
||||
buf,
|
||||
&mut class,
|
||||
&mut style,
|
||||
|
||||
@@ -125,14 +125,14 @@ impl Render for InertElement {
|
||||
type State = InertElementState;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let el = Rndr::create_element_from_html(self.html.clone());
|
||||
let el = Rndr::create_element_from_html(&self.html);
|
||||
InertElementState(self.html, el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let InertElementState(prev, el) = state;
|
||||
if &self.html != prev {
|
||||
let mut new_el = Rndr::create_element_from_html(self.html.clone());
|
||||
let mut new_el = Rndr::create_element_from_html(&self.html);
|
||||
el.insert_before_this(&mut new_el);
|
||||
el.unmount();
|
||||
*el = new_el;
|
||||
|
||||
@@ -209,15 +209,6 @@ where
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (name, mut f) = self;
|
||||
|
||||
let prev_name = state.name;
|
||||
let prev_state = state.effect.take_value();
|
||||
if let Some((list, prev_include)) = &prev_state {
|
||||
if prev_name != name && *prev_include {
|
||||
Rndr::remove_class(list, prev_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Name might've updated:
|
||||
state.name = name;
|
||||
let mut first_run = true;
|
||||
@@ -241,7 +232,7 @@ where
|
||||
}
|
||||
}
|
||||
},
|
||||
prev_state,
|
||||
state.effect.take_value(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,9 @@ use crate::{
|
||||
view::{Mountable, ToTemplate},
|
||||
};
|
||||
use linear_map::LinearMap;
|
||||
use once_cell::unsync::Lazy;
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
cell::{LazyCell, RefCell},
|
||||
};
|
||||
use std::{any::TypeId, borrow::Cow, cell::RefCell};
|
||||
use wasm_bindgen::{intern, prelude::Closure, JsCast, JsValue};
|
||||
use web_sys::{AddEventListenerOptions, Comment, HtmlTemplateElement};
|
||||
|
||||
@@ -24,7 +21,6 @@ pub struct Dom;
|
||||
|
||||
thread_local! {
|
||||
pub(crate) static GLOBAL_EVENTS: RefCell<FxHashSet<Cow<'static, str>>> = Default::default();
|
||||
pub static TEMPLATE_CACHE: RefCell<Vec<(Cow<'static, str>, web_sys::Element)>> = Default::default();
|
||||
}
|
||||
|
||||
pub type Node = web_sys::Node;
|
||||
@@ -61,7 +57,7 @@ impl Dom {
|
||||
|
||||
pub fn create_placeholder() -> Placeholder {
|
||||
thread_local! {
|
||||
static COMMENT: LazyCell<Comment> = LazyCell::new(|| {
|
||||
static COMMENT: Lazy<Comment> = Lazy::new(|| {
|
||||
document().create_comment("")
|
||||
});
|
||||
}
|
||||
@@ -285,10 +281,9 @@ impl Dom {
|
||||
let cb = send_wrapper::SendWrapper::new(cb);
|
||||
move |el: &Element| {
|
||||
or_debug!(
|
||||
el.remove_event_listener_with_callback_and_bool(
|
||||
el.remove_event_listener_with_callback(
|
||||
intern(&name),
|
||||
cb.as_ref().unchecked_ref(),
|
||||
true
|
||||
cb.as_ref().unchecked_ref()
|
||||
),
|
||||
el,
|
||||
"removeEventListener"
|
||||
@@ -456,8 +451,8 @@ impl Dom {
|
||||
V: ToTemplate + 'static,
|
||||
{
|
||||
thread_local! {
|
||||
static TEMPLATE_ELEMENT: LazyCell<HtmlTemplateElement> =
|
||||
LazyCell::new(|| document().create_element(Dom::intern("template")).unwrap().unchecked_into());
|
||||
static TEMPLATE_ELEMENT: Lazy<HtmlTemplateElement> =
|
||||
Lazy::new(|| document().create_element("template").unwrap().unchecked_into());
|
||||
static TEMPLATES: RefCell<LinearMap<TypeId, HtmlTemplateElement>> = Default::default();
|
||||
}
|
||||
|
||||
@@ -492,66 +487,13 @@ impl Dom {
|
||||
.unchecked_into()
|
||||
}
|
||||
|
||||
pub fn create_element_from_html(html: Cow<'static, str>) -> Element {
|
||||
let tpl = TEMPLATE_CACHE.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
if let Some(tpl_content) = cache.iter().find_map(|(key, tpl)| {
|
||||
(html == *key)
|
||||
.then_some(Self::clone_template(tpl.unchecked_ref()))
|
||||
}) {
|
||||
tpl_content
|
||||
} else {
|
||||
let tpl = document()
|
||||
.create_element(Self::intern("template"))
|
||||
.unwrap();
|
||||
tpl.set_inner_html(&html);
|
||||
let tpl_content = Self::clone_template(tpl.unchecked_ref());
|
||||
cache.push((html, tpl));
|
||||
tpl_content
|
||||
}
|
||||
});
|
||||
pub fn create_element_from_html(html: &str) -> Element {
|
||||
// TODO can be optimized to cache HTML strings or cache <template>?
|
||||
let tpl = document().create_element("template").unwrap();
|
||||
tpl.set_inner_html(html);
|
||||
let tpl = Self::clone_template(tpl.unchecked_ref());
|
||||
tpl.first_element_child().unwrap_or(tpl)
|
||||
}
|
||||
|
||||
pub fn create_svg_element_from_html(html: Cow<'static, str>) -> Element {
|
||||
let tpl = TEMPLATE_CACHE.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
if let Some(tpl_content) = cache.iter().find_map(|(key, tpl)| {
|
||||
(html == *key)
|
||||
.then_some(Self::clone_template(tpl.unchecked_ref()))
|
||||
}) {
|
||||
tpl_content
|
||||
} else {
|
||||
let tpl = document()
|
||||
.create_element(Self::intern("template"))
|
||||
.unwrap();
|
||||
let svg = document()
|
||||
.create_element_ns(
|
||||
Some(Self::intern("http://www.w3.org/2000/svg")),
|
||||
Self::intern("svg"),
|
||||
)
|
||||
.unwrap();
|
||||
let g = document()
|
||||
.create_element_ns(
|
||||
Some(Self::intern("http://www.w3.org/2000/svg")),
|
||||
Self::intern("g"),
|
||||
)
|
||||
.unwrap();
|
||||
g.set_inner_html(&html);
|
||||
svg.append_child(&g).unwrap();
|
||||
tpl.unchecked_ref::<TemplateElement>()
|
||||
.content()
|
||||
.append_child(&svg)
|
||||
.unwrap();
|
||||
let tpl_content = Self::clone_template(tpl.unchecked_ref());
|
||||
cache.push((html, tpl));
|
||||
tpl_content
|
||||
}
|
||||
});
|
||||
|
||||
let svg = tpl.first_element_child().unwrap();
|
||||
svg.first_element_child().unwrap_or(svg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for Node {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
use crate::{
|
||||
html::{
|
||||
attribute::{any_attribute::AnyAttribute, Attribute},
|
||||
attribute::Attribute,
|
||||
element::{ElementType, ElementWithChildren, HtmlElement},
|
||||
},
|
||||
hydration::Cursor,
|
||||
prelude::{AddAnyAttr, Mountable},
|
||||
renderer::{
|
||||
dom::{Element, Node},
|
||||
CastFrom, Rndr,
|
||||
},
|
||||
view::{Position, PositionState, Render, RenderHtml},
|
||||
view::Render,
|
||||
};
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
use std::fmt::Debug;
|
||||
|
||||
macro_rules! svg_elements {
|
||||
($($tag:ident [$($attr:ty),*]),* $(,)?) => {
|
||||
@@ -191,124 +185,3 @@ impl ElementType for Use {
|
||||
}
|
||||
|
||||
impl ElementWithChildren for Use {}
|
||||
|
||||
/// An element that contains no interactivity, and whose contents can be known at compile time.
|
||||
pub struct InertElement {
|
||||
html: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl InertElement {
|
||||
/// Creates a new inert svg element.
|
||||
pub fn new(html: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self { html: html.into() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained view state for [`InertElement`].
|
||||
pub struct InertElementState(Cow<'static, str>, Element);
|
||||
|
||||
impl Mountable for InertElementState {
|
||||
fn unmount(&mut self) {
|
||||
self.1.unmount();
|
||||
}
|
||||
|
||||
fn mount(&mut self, parent: &Element, marker: Option<&Node>) {
|
||||
self.1.mount(parent, marker)
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
self.1.insert_before_this(child)
|
||||
}
|
||||
|
||||
fn elements(&self) -> Vec<crate::renderer::types::Element> {
|
||||
vec![self.1.clone()]
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for InertElement {
|
||||
type State = InertElementState;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let el = Rndr::create_svg_element_from_html(self.html.clone());
|
||||
InertElementState(self.html, el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let InertElementState(prev, el) = state;
|
||||
if &self.html != prev {
|
||||
let mut new_el =
|
||||
Rndr::create_svg_element_from_html(self.html.clone());
|
||||
el.insert_before_this(&mut new_el);
|
||||
el.unmount();
|
||||
*el = new_el;
|
||||
*prev = self.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAnyAttr for InertElement {
|
||||
type Output<SomeNewAttr: Attribute> = Self;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
{
|
||||
panic!(
|
||||
"InertElement does not support adding attributes. It should only \
|
||||
be used as a child, and not returned at the top level."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for InertElement {
|
||||
type AsyncOutput = Self;
|
||||
type Owned = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.html.len()
|
||||
}
|
||||
|
||||
fn dry_resolve(&mut self) {}
|
||||
|
||||
async fn resolve(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
_escape: bool,
|
||||
_mark_branches: bool,
|
||||
_extra_attrs: Vec<AnyAttribute>,
|
||||
) {
|
||||
buf.push_str(&self.html);
|
||||
*position = Position::NextChild;
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let curr_position = position.get();
|
||||
if curr_position == Position::FirstChild {
|
||||
cursor.child();
|
||||
} else if curr_position != Position::Current {
|
||||
cursor.sibling();
|
||||
}
|
||||
let el = crate::renderer::types::Element::cast_from(cursor.current())
|
||||
.unwrap();
|
||||
position.set(Position::NextChild);
|
||||
InertElementState(self.html, el)
|
||||
}
|
||||
|
||||
fn into_owned(self) -> Self::Owned {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,12 +53,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoFragment for AnyView {
|
||||
fn into_fragment(self) -> Fragment {
|
||||
Fragment::new(vec![self])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoFragment for Vec<T>
|
||||
where
|
||||
T: IntoAny,
|
||||
|
||||
@@ -437,17 +437,6 @@ pub trait ToTemplate {
|
||||
inner_html: &mut String,
|
||||
position: &mut Position,
|
||||
);
|
||||
|
||||
/// Renders a view type to a template in attribute position.
|
||||
fn to_template_attribute(
|
||||
buf: &mut String,
|
||||
class: &mut String,
|
||||
style: &mut String,
|
||||
inner_html: &mut String,
|
||||
position: &mut Position,
|
||||
) {
|
||||
Self::to_template(buf, class, style, inner_html, position);
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps track of what position the item currently being hydrated is in, relative to its siblings
|
||||
|
||||
@@ -103,15 +103,6 @@ impl ToTemplate for () {
|
||||
) {
|
||||
buf.push_str("<!>");
|
||||
}
|
||||
|
||||
fn to_template_attribute(
|
||||
_buf: &mut String,
|
||||
_class: &mut String,
|
||||
_style: &mut String,
|
||||
_inner_html: &mut String,
|
||||
_position: &mut Position,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Render> Render for (A,) {
|
||||
|
||||
Reference in New Issue
Block a user