mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 11:21:55 -05:00
Compare commits
757 Commits
v0.7.0-bet
...
2901/trigg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60f5c68ec2 | ||
|
|
fd19dd38f6 | ||
|
|
b42d86e600 | ||
|
|
8845eec553 | ||
|
|
b3c83f7700 | ||
|
|
4dea1195e2 | ||
|
|
92ea39ddac | ||
|
|
05e08166c4 | ||
|
|
827cc0bdfa | ||
|
|
57bd343f4a | ||
|
|
4a76aead68 | ||
|
|
48c2148589 | ||
|
|
32bea69c28 | ||
|
|
f3c57f8bce | ||
|
|
000896b2f7 | ||
|
|
88004e5042 | ||
|
|
6001a93475 | ||
|
|
4784b2ddab | ||
|
|
32b4cd008f | ||
|
|
823f8b51be | ||
|
|
209743d6bc | ||
|
|
b93a88accc | ||
|
|
dc2314d5e2 | ||
|
|
33aa676854 | ||
|
|
4a3b3ffb8a | ||
|
|
ee5cbf1891 | ||
|
|
8fcf3544a8 | ||
|
|
2b8e987cb8 | ||
|
|
998165148b | ||
|
|
c80eff1098 | ||
|
|
cd8f2c2153 | ||
|
|
cb0abff2d5 | ||
|
|
3b1b2e2dcc | ||
|
|
7831e4ad05 | ||
|
|
e7bb859cd9 | ||
|
|
9fc26e609c | ||
|
|
4f1ee65e6c | ||
|
|
ceff827a77 | ||
|
|
a7db918775 | ||
|
|
be20ecd366 | ||
|
|
5790d8ad12 | ||
|
|
7dc58e248c | ||
|
|
7b03e63b23 | ||
|
|
55fd7c6421 | ||
|
|
ba1ea4c2bb | ||
|
|
6a4fc96835 | ||
|
|
58476bb98e | ||
|
|
88d4f14541 | ||
|
|
6bba233ba7 | ||
|
|
1d99764740 | ||
|
|
53cc479c14 | ||
|
|
d3707d9b88 | ||
|
|
1df4076fd2 | ||
|
|
28337bb6c9 | ||
|
|
c5dea52e69 | ||
|
|
d5096ff2e6 | ||
|
|
84734f1110 | ||
|
|
7094308287 | ||
|
|
3b88c8ccd2 | ||
|
|
b932bd5e04 | ||
|
|
a0638b786c | ||
|
|
1033133d3a | ||
|
|
ba40560ad7 | ||
|
|
78dc8b4410 | ||
|
|
c6bf525270 | ||
|
|
fcdfd617f5 | ||
|
|
d9c8d4ff66 | ||
|
|
621f112f4c | ||
|
|
cfe2341dec | ||
|
|
5ed2cc9596 | ||
|
|
47b07b0143 | ||
|
|
0793d56648 | ||
|
|
2dde9738b2 | ||
|
|
1b276e1e02 | ||
|
|
9771619b0d | ||
|
|
382a36406c | ||
|
|
a898d3f2f7 | ||
|
|
951f4a73ee | ||
|
|
2a26a648ba | ||
|
|
66d7cb5d12 | ||
|
|
aa49ad760b | ||
|
|
3469e9335c | ||
|
|
62408d9202 | ||
|
|
db1c15f4e4 | ||
|
|
3f751551a8 | ||
|
|
2470036f57 | ||
|
|
e01dfbf497 | ||
|
|
49c1661f92 | ||
|
|
b9dfd9a5ae | ||
|
|
17a150b3bf | ||
|
|
c860f524ad | ||
|
|
20af4928b2 | ||
|
|
7b62ad44d2 | ||
|
|
5657abc07d | ||
|
|
9fc351ceac | ||
|
|
893d47f1c5 | ||
|
|
3c2a2304e4 | ||
|
|
3de4b7b982 | ||
|
|
36957cb569 | ||
|
|
27dbadb7d2 | ||
|
|
84590b98ed | ||
|
|
0222182286 | ||
|
|
7ed4d08dab | ||
|
|
4fa2e58551 | ||
|
|
5fe58369f5 | ||
|
|
10860ebb1e | ||
|
|
887eb99cf6 | ||
|
|
9f99571b28 | ||
|
|
b3e2040ec9 | ||
|
|
693861434c | ||
|
|
d1248d3faf | ||
|
|
f1fae63064 | ||
|
|
f7053ac960 | ||
|
|
8670365594 | ||
|
|
e0cc6fd7b9 | ||
|
|
84457b45ff | ||
|
|
8c959d3e24 | ||
|
|
288da232ac | ||
|
|
ef52e8620b | ||
|
|
a8c5ce2722 | ||
|
|
a40ba9a39e | ||
|
|
83a110fb85 | ||
|
|
5deedf721e | ||
|
|
0ae67cf122 | ||
|
|
97e0222061 | ||
|
|
a7aedee4fd | ||
|
|
f180941b7b | ||
|
|
83cb3cb764 | ||
|
|
f174688974 | ||
|
|
1f4c410f78 | ||
|
|
3dbedfc871 | ||
|
|
026152d20e | ||
|
|
c70009243a | ||
|
|
dcdad73476 | ||
|
|
42a8e49e1a | ||
|
|
9ba894764e | ||
|
|
1d97494e19 | ||
|
|
982bfd8011 | ||
|
|
a0ad927097 | ||
|
|
4845459511 | ||
|
|
54e4205541 | ||
|
|
25f0186098 | ||
|
|
00fb8f29d3 | ||
|
|
82ea4eb7ce | ||
|
|
34382c0c23 | ||
|
|
605e4b1eec | ||
|
|
71ca02a432 | ||
|
|
ac751dd0d3 | ||
|
|
114df8797c | ||
|
|
35b457aa82 | ||
|
|
c43b71d3fa | ||
|
|
5ad17d6b6c | ||
|
|
805be42f7a | ||
|
|
bb63a1521e | ||
|
|
05cd1bc6f0 | ||
|
|
84ac64284c | ||
|
|
8beb988ecc | ||
|
|
05999069b7 | ||
|
|
dbb4c79d9c | ||
|
|
d1d6126259 | ||
|
|
0e979a0767 | ||
|
|
3a4ad07a91 | ||
|
|
f2f35cd785 | ||
|
|
c953432659 | ||
|
|
27f2e60d16 | ||
|
|
9330cf23b1 | ||
|
|
04f5207457 | ||
|
|
c88409a333 | ||
|
|
a2d3d9431c | ||
|
|
213365e4e9 | ||
|
|
ab3e94dafa | ||
|
|
3e05b5bcb4 | ||
|
|
632ce0f401 | ||
|
|
93c893f4b3 | ||
|
|
4fa72a94fb | ||
|
|
24775fb59b | ||
|
|
6c749f5e24 | ||
|
|
c22f20ac28 | ||
|
|
11011c2bda | ||
|
|
a51faea9a9 | ||
|
|
dcc43e574b | ||
|
|
6cc0604497 | ||
|
|
8c375534bb | ||
|
|
f2d6375d93 | ||
|
|
5fb80aaa40 | ||
|
|
29b0dca1d8 | ||
|
|
a6a65ba562 | ||
|
|
260c624461 | ||
|
|
0f50aced26 | ||
|
|
1aa2752842 | ||
|
|
b92bfa4ea7 | ||
|
|
30cf1167f2 | ||
|
|
1c05389707 | ||
|
|
1534dd5261 | ||
|
|
8b9685e01d | ||
|
|
d9b590b8e0 | ||
|
|
25bfc27544 | ||
|
|
89bbdc58af | ||
|
|
4fb00e29d6 | ||
|
|
54401c6f69 | ||
|
|
6a705e2a21 | ||
|
|
43421c56d5 | ||
|
|
4c4d3dcfa3 | ||
|
|
55053da00c | ||
|
|
be83b5f27e | ||
|
|
a4ce79769a | ||
|
|
e3f64188c2 | ||
|
|
f7abe727d9 | ||
|
|
eb29d84169 | ||
|
|
200047a8bc | ||
|
|
634ac1c4a3 | ||
|
|
efe832e39a | ||
|
|
1f2b13a976 | ||
|
|
d4ec5e187b | ||
|
|
4fe7fe725f | ||
|
|
a1ca8549a1 | ||
|
|
e7a8067f9b | ||
|
|
6be090079f | ||
|
|
8635887ca7 | ||
|
|
e3482b433b | ||
|
|
e6c2f8c614 | ||
|
|
28fcfe4a46 | ||
|
|
75336bc265 | ||
|
|
f4f129caaf | ||
|
|
873aec5787 | ||
|
|
a2385e4c42 | ||
|
|
d24f97b59f | ||
|
|
51f368c5c5 | ||
|
|
4107203da2 | ||
|
|
efb699a319 | ||
|
|
93e6456e19 | ||
|
|
b24ae7a5e3 | ||
|
|
bf8d2e079c | ||
|
|
7752ab78e3 | ||
|
|
64bc2580ff | ||
|
|
ddb596feb5 | ||
|
|
dac4589194 | ||
|
|
8f0a8e05b4 | ||
|
|
05d01141c5 | ||
|
|
66d6038f2d | ||
|
|
3b09312e1a | ||
|
|
62cb361031 | ||
|
|
04c67cb8b6 | ||
|
|
efd060c955 | ||
|
|
6290c42159 | ||
|
|
0a89f151be | ||
|
|
c72c2f4803 | ||
|
|
c771ab7e71 | ||
|
|
2c4f11b238 | ||
|
|
12a9e06c5e | ||
|
|
3515469835 | ||
|
|
e5c159f7a5 | ||
|
|
6590749956 | ||
|
|
db8f5e4899 | ||
|
|
6ca3639c3e | ||
|
|
37db7b5d0a | ||
|
|
8ac1564b90 | ||
|
|
e2721d53bd | ||
|
|
e1f3be6416 | ||
|
|
9536480739 | ||
|
|
5d3a1752c4 | ||
|
|
4b539b524b | ||
|
|
67fe4cc540 | ||
|
|
fa731d5018 | ||
|
|
ccf6703274 | ||
|
|
504c958001 | ||
|
|
f7b16b726b | ||
|
|
e9c7b50dfd | ||
|
|
208ab97867 | ||
|
|
4a0f173bb5 | ||
|
|
0cf3113812 | ||
|
|
87f9fa23d5 | ||
|
|
746bf8e453 | ||
|
|
8b9bcffbb9 | ||
|
|
dc80e387e3 | ||
|
|
2006eca1a0 | ||
|
|
1dae77d6b4 | ||
|
|
2f58191a56 | ||
|
|
a68653b385 | ||
|
|
d7ca969848 | ||
|
|
fd48a61eef | ||
|
|
52a3f84de5 | ||
|
|
f8283f4674 | ||
|
|
989f2989fa | ||
|
|
33a3708f91 | ||
|
|
4eea1f046d | ||
|
|
8f46288973 | ||
|
|
059c8abd2f | ||
|
|
6885777c75 | ||
|
|
ddc7abf081 | ||
|
|
180511e9bb | ||
|
|
381ff8a7b0 | ||
|
|
3ed1ad7b7f | ||
|
|
2ccf5e99a9 | ||
|
|
055701ebf6 | ||
|
|
88af893703 | ||
|
|
ce4fe632a2 | ||
|
|
c76208aad0 | ||
|
|
514c51ca30 | ||
|
|
7e3781b5dd | ||
|
|
97dc3cc2e5 | ||
|
|
61e51cbe1c | ||
|
|
efa6d603f9 | ||
|
|
da045f7358 | ||
|
|
8502745036 | ||
|
|
0a5e6fd85a | ||
|
|
64fc6cd514 | ||
|
|
a2d8fde8cf | ||
|
|
44eae4c2ed | ||
|
|
38d51b01d7 | ||
|
|
61876dff10 | ||
|
|
c676cf921d | ||
|
|
fc59cdaf61 | ||
|
|
081f4ec550 | ||
|
|
598c59b9c2 | ||
|
|
9de6c5bb4a | ||
|
|
f65eaec9ba | ||
|
|
95756aa2f7 | ||
|
|
fd121fd8c1 | ||
|
|
c1877354f0 | ||
|
|
be92dc56aa | ||
|
|
165a593b32 | ||
|
|
18b33c7606 | ||
|
|
d2ee093132 | ||
|
|
83e0438527 | ||
|
|
095dc78893 | ||
|
|
3ebea79e05 | ||
|
|
fe7c7c3a99 | ||
|
|
8b142c72f0 | ||
|
|
70655b57b1 | ||
|
|
c6192badfb | ||
|
|
5b7f5e3db3 | ||
|
|
ae14644806 | ||
|
|
7ca810d8bd | ||
|
|
04e09d2005 | ||
|
|
2916873985 | ||
|
|
2a558aa3f0 | ||
|
|
36d16d9253 | ||
|
|
722fd0f6c2 | ||
|
|
a42e371e79 | ||
|
|
11119144d2 | ||
|
|
a0b158f016 | ||
|
|
8dc7338b85 | ||
|
|
737949cff6 | ||
|
|
d7e17a2ec9 | ||
|
|
7c5b7fcbb1 | ||
|
|
1182aff410 | ||
|
|
bdcd4cb1cc | ||
|
|
c74a791d9f | ||
|
|
772a837050 | ||
|
|
92552deb0d | ||
|
|
417d345b83 | ||
|
|
3fb2d49d89 | ||
|
|
27feaf4309 | ||
|
|
35f489a52e | ||
|
|
ba8bd2bc82 | ||
|
|
76506c03e1 | ||
|
|
4323e30133 | ||
|
|
81c0947ce5 | ||
|
|
309a3d504a | ||
|
|
2a236e043a | ||
|
|
63f8da2fb5 | ||
|
|
c9e32b66bf | ||
|
|
a32c71539d | ||
|
|
f7ee0c4764 | ||
|
|
7034375cdd | ||
|
|
3c9c5aaf83 | ||
|
|
ce832cef21 | ||
|
|
10230d6d65 | ||
|
|
e4d25608df | ||
|
|
bd1601e892 | ||
|
|
602ac60a85 | ||
|
|
9e4c0b86f2 | ||
|
|
4a80c8b65b | ||
|
|
f191bb8324 | ||
|
|
1ff1d48e6e | ||
|
|
df6a4628c3 | ||
|
|
e28e5ceb1e | ||
|
|
e69f62b939 | ||
|
|
2c48b07186 | ||
|
|
0d867ba016 | ||
|
|
3f83ad7dda | ||
|
|
50403846c9 | ||
|
|
4ead16e5d3 | ||
|
|
32f77cc42b | ||
|
|
d8834a0423 | ||
|
|
0d665c9c05 | ||
|
|
a03d74494d | ||
|
|
131c83e28e | ||
|
|
6d93185478 | ||
|
|
202abd1d35 | ||
|
|
a50c6b0140 | ||
|
|
705ea3a3bb | ||
|
|
cb788758df | ||
|
|
1afdc4fe1e | ||
|
|
3382047857 | ||
|
|
a29ffc8dcb | ||
|
|
a18dd6dfd7 | ||
|
|
626bcdc9ae | ||
|
|
d6dce76725 | ||
|
|
36272a0b1b | ||
|
|
96c956efdf | ||
|
|
29cf1f4814 | ||
|
|
39c3a63787 | ||
|
|
068865b7de | ||
|
|
fa8bb15a67 | ||
|
|
faa481f2b6 | ||
|
|
b41d988865 | ||
|
|
025c28b489 | ||
|
|
0c7c7c9b38 | ||
|
|
b109c3e9a3 | ||
|
|
0a559935e7 | ||
|
|
bccc05fec8 | ||
|
|
e0f98dc0fd | ||
|
|
5d9bd8f913 | ||
|
|
0a41ae9a5e | ||
|
|
fbc6be922d | ||
|
|
b5551863fe | ||
|
|
14b3877293 | ||
|
|
98ea18009d | ||
|
|
d133cff092 | ||
|
|
48028b476a | ||
|
|
404ad50bd3 | ||
|
|
b89fbe027b | ||
|
|
0ba53afa08 | ||
|
|
c384b53a0f | ||
|
|
2f53e09bb6 | ||
|
|
319eefb169 | ||
|
|
949f43d145 | ||
|
|
a47759007f | ||
|
|
095faf15b1 | ||
|
|
f9eb562050 | ||
|
|
7f57b88e8d | ||
|
|
8a8862be9e | ||
|
|
619dc59e1d | ||
|
|
5f49504137 | ||
|
|
ca68fa5a3d | ||
|
|
e6a472b467 | ||
|
|
f8da9e30e0 | ||
|
|
984ede8887 | ||
|
|
c3656416a2 | ||
|
|
7ecfbd9109 | ||
|
|
531c39759a | ||
|
|
f5d06577f4 | ||
|
|
39902d1e66 | ||
|
|
7def5f65ed | ||
|
|
6b60d48203 | ||
|
|
9ef51166d3 | ||
|
|
8da6bbc3be | ||
|
|
3c39674622 | ||
|
|
914b07491e | ||
|
|
1d2d11b83d | ||
|
|
07e878adf7 | ||
|
|
f32d43ce94 | ||
|
|
65e3c57ed1 | ||
|
|
2e40bace88 | ||
|
|
b9945e0ce1 | ||
|
|
d7f70214b9 | ||
|
|
adf57f5771 | ||
|
|
bae79e2b2c | ||
|
|
e2b1210461 | ||
|
|
7c24b7482d | ||
|
|
25c66a4624 | ||
|
|
71ddacef8e | ||
|
|
338b01bee3 | ||
|
|
a36f22e439 | ||
|
|
56977411f2 | ||
|
|
0fc47e3a35 | ||
|
|
caf797dba0 | ||
|
|
2f54d937a1 | ||
|
|
40c1f38a07 | ||
|
|
402d6297f4 | ||
|
|
93734a5222 | ||
|
|
770d02d8e6 | ||
|
|
e275862a20 | ||
|
|
17f1d25d03 | ||
|
|
0a99a378aa | ||
|
|
14b7073863 | ||
|
|
4e4deef144 | ||
|
|
c360f0ed0d | ||
|
|
88ab9693db | ||
|
|
6dfea0b0a2 | ||
|
|
9fd881603f | ||
|
|
9666c9c0c5 | ||
|
|
9e8b304b8a | ||
|
|
064ccce5b1 | ||
|
|
2e31177f62 | ||
|
|
4215cef04b | ||
|
|
de3dd3c296 | ||
|
|
846ff2fefb | ||
|
|
6003212f6e | ||
|
|
054cff7883 | ||
|
|
ce5738d7c4 | ||
|
|
47331b5c8d | ||
|
|
a6cee3b1e9 | ||
|
|
43c0e384c4 | ||
|
|
db654cbfda | ||
|
|
e13b1561d8 | ||
|
|
02f76dec35 | ||
|
|
4bd99a41e5 | ||
|
|
85d29a5af5 | ||
|
|
4d54574f9e | ||
|
|
f6c7ac473a | ||
|
|
747d847183 | ||
|
|
8dd63a402b | ||
|
|
694eccbadc | ||
|
|
24f2e71563 | ||
|
|
1766bfedb9 | ||
|
|
242d35cc37 | ||
|
|
85b9f87620 | ||
|
|
db33bc2e61 | ||
|
|
a1329ea044 | ||
|
|
050bf8f821 | ||
|
|
1a68743fcc | ||
|
|
2925db8676 | ||
|
|
13d5f12d7f | ||
|
|
3d9c295613 | ||
|
|
b2c0068e2c | ||
|
|
94a3f7c092 | ||
|
|
330dcfeb7c | ||
|
|
f7bbec5f06 | ||
|
|
8815529955 | ||
|
|
12db58a7e0 | ||
|
|
83c9edde26 | ||
|
|
2037a6966a | ||
|
|
4f041f5a5e | ||
|
|
6467e067ef | ||
|
|
3814879d80 | ||
|
|
5e16ae6a26 | ||
|
|
6d474713f6 | ||
|
|
0d47399424 | ||
|
|
ae254836d7 | ||
|
|
2dd5efc5d0 | ||
|
|
15eeda9c7a | ||
|
|
1a739015e1 | ||
|
|
8385287123 | ||
|
|
c4aa3ba1ba | ||
|
|
ce5f2c81ed | ||
|
|
941689fc5b | ||
|
|
961bf89a8b | ||
|
|
d360cc280f | ||
|
|
bb7bb8f4c2 | ||
|
|
b29b8fb5ff | ||
|
|
4ffa3c46b6 | ||
|
|
32294d6cab | ||
|
|
46d286755e | ||
|
|
b936e0352f | ||
|
|
b5bd70ab94 | ||
|
|
0dd1932b7f | ||
|
|
f5d203f0c9 | ||
|
|
1bc0b414e3 | ||
|
|
d6e19c0a60 | ||
|
|
fc60d6b2d7 | ||
|
|
292e7c1f27 | ||
|
|
1da84db1aa | ||
|
|
535e3e3880 | ||
|
|
109244b28b | ||
|
|
fd048295a4 | ||
|
|
26cf4848db | ||
|
|
757a5c73c3 | ||
|
|
da496def16 | ||
|
|
3a755bd8c3 | ||
|
|
e514f7144d | ||
|
|
b881167b8f | ||
|
|
1e9d345831 | ||
|
|
7f7bba6ea3 | ||
|
|
015a4b63ec | ||
|
|
dcec7af4f3 | ||
|
|
5bc97654dc | ||
|
|
2788d93e96 | ||
|
|
604043b4d8 | ||
|
|
ab28c80593 | ||
|
|
49da073fed | ||
|
|
3629302f88 | ||
|
|
274e31018b | ||
|
|
802fcc5c2a | ||
|
|
da084a2ece | ||
|
|
d9f6836933 | ||
|
|
d8d2fdac5d | ||
|
|
9818e7cb68 | ||
|
|
986fbe5328 | ||
|
|
711175a760 | ||
|
|
00a536a5dc | ||
|
|
a7b1152910 | ||
|
|
cfba7a2797 | ||
|
|
cebe744a84 | ||
|
|
e93a34a2c9 | ||
|
|
9ec30d71d2 | ||
|
|
3c13280bf6 | ||
|
|
45fd9423f8 | ||
|
|
7a92208c4f | ||
|
|
89b972e3c5 | ||
|
|
8dac92b251 | ||
|
|
b24eaedfe9 | ||
|
|
4336051f78 | ||
|
|
97ce5adb8e | ||
|
|
20fb5454b0 | ||
|
|
aac607f338 | ||
|
|
738986415d | ||
|
|
3406446ebd | ||
|
|
21dd7e9c76 | ||
|
|
9bab4da172 | ||
|
|
420dccda60 | ||
|
|
53b22a9b74 | ||
|
|
e68730d15f | ||
|
|
11d134c4ba | ||
|
|
2239f04f6b | ||
|
|
78e5a7ebc3 | ||
|
|
0148d92f48 | ||
|
|
ab67bea7ec | ||
|
|
0beef3b2e0 | ||
|
|
81fc7e6ada | ||
|
|
75d6763f4e | ||
|
|
da4d2cf538 | ||
|
|
2470637b0b | ||
|
|
2934c295b5 | ||
|
|
789eef914d | ||
|
|
782cb93743 | ||
|
|
8642c563d8 | ||
|
|
755fbd3866 | ||
|
|
d83471e02b | ||
|
|
2dd2bb5958 | ||
|
|
12f2cec5c7 | ||
|
|
a41bf2784f | ||
|
|
ebdd31cd9f | ||
|
|
acec3bb313 | ||
|
|
464f157186 | ||
|
|
b53e4d8ff8 | ||
|
|
cd438e0bcf | ||
|
|
13da1e743d | ||
|
|
0c9167fd30 | ||
|
|
52da0e43ac | ||
|
|
dad91f5960 | ||
|
|
72e97047a5 | ||
|
|
883fd57fe1 | ||
|
|
42b99dd912 | ||
|
|
851e1f73fd | ||
|
|
e11eea1af1 | ||
|
|
f508cc4510 | ||
|
|
e4f3cf9cca | ||
|
|
60d883a26c | ||
|
|
add3be0ff5 | ||
|
|
a01640cafd | ||
|
|
e837e9fded | ||
|
|
e0e67360aa | ||
|
|
439deea066 | ||
|
|
e5f5710f46 | ||
|
|
8626db27d7 | ||
|
|
ec3f0933fe | ||
|
|
b50de3a005 | ||
|
|
aa878534ad | ||
|
|
603f9f96c4 | ||
|
|
f78e675506 | ||
|
|
cc2714c03d | ||
|
|
c06110128b | ||
|
|
d7c62622ae | ||
|
|
1edec6c36a | ||
|
|
c5049ca1bb | ||
|
|
f69dbb48ca | ||
|
|
5feaf1aea6 | ||
|
|
ec3ab6a355 | ||
|
|
100ed7d926 | ||
|
|
88b93f40f9 | ||
|
|
b8b77138ea | ||
|
|
20c29cab89 | ||
|
|
54fd74839a | ||
|
|
ea3790d91c | ||
|
|
f5935c6333 | ||
|
|
c8e5e1b16b | ||
|
|
a12c707f3f | ||
|
|
6d9906111d | ||
|
|
5ea314c998 | ||
|
|
2bc04444e1 | ||
|
|
b41fde3ff9 | ||
|
|
c29081b12a | ||
|
|
2fefc8b4bf | ||
|
|
72b43d1e2b | ||
|
|
39607adc94 | ||
|
|
30c1cd921b | ||
|
|
abfe3cabd2 | ||
|
|
16bd2942db | ||
|
|
13cccced06 | ||
|
|
db4c1cb4b3 | ||
|
|
9cdd8cac15 | ||
|
|
84ebdc1b92 | ||
|
|
9f02cc8cc1 | ||
|
|
c3b9932172 | ||
|
|
dbd9951a85 | ||
|
|
6eb8b44fff | ||
|
|
4fa31be5dc | ||
|
|
b46dffb729 | ||
|
|
ca54762806 | ||
|
|
f122f9109f | ||
|
|
f894d6e4f6 | ||
|
|
4cc925c950 | ||
|
|
21e53042e8 | ||
|
|
4d3fb37b35 | ||
|
|
d3a21c922d | ||
|
|
317f90e1e3 | ||
|
|
26869a78a0 | ||
|
|
f46f864f05 | ||
|
|
b21f1853c6 | ||
|
|
1454c5d272 | ||
|
|
c1f4616a31 | ||
|
|
a3c3478831 | ||
|
|
1ca8a9189c | ||
|
|
9e276a8879 | ||
|
|
53703f208a | ||
|
|
9a60b21a0a | ||
|
|
524ed395fa | ||
|
|
5bc8c4e0d3 | ||
|
|
7f7003f7f1 | ||
|
|
ddf2ac0cf7 | ||
|
|
992e2bce78 | ||
|
|
6c2469ec3a | ||
|
|
a7162d7907 | ||
|
|
f584154156 | ||
|
|
13464b10c9 | ||
|
|
696bf14d13 | ||
|
|
be92bab3e5 | ||
|
|
4bb2bc4797 | ||
|
|
a8adf8eea2 | ||
|
|
1a7da39fb7 | ||
|
|
201adb7406 | ||
|
|
4df42cbc60 | ||
|
|
44a0a0a93a | ||
|
|
66e1e6d7a1 | ||
|
|
8252c4a977 | ||
|
|
6a2eafcbc6 | ||
|
|
b49a13f8c1 | ||
|
|
0d5c67408f | ||
|
|
1eddd5a5f1 | ||
|
|
ca1e62c0b9 | ||
|
|
043cd7dc61 | ||
|
|
68486cfb72 | ||
|
|
eea971b9fe | ||
|
|
d726b56b71 | ||
|
|
0fddfb4823 | ||
|
|
17732a6e6a | ||
|
|
c8441f0f00 | ||
|
|
ff4cde0764 | ||
|
|
1d38439bd8 | ||
|
|
9ca1cba504 | ||
|
|
63dacdcc95 | ||
|
|
61f5294f67 | ||
|
|
0149632a4c | ||
|
|
96384ed116 | ||
|
|
f56023bb25 | ||
|
|
6bb5d58369 | ||
|
|
d50012f8d4 | ||
|
|
c9d4ea9307 | ||
|
|
77c74bccbb | ||
|
|
528d1eae65 | ||
|
|
5809c8f699 | ||
|
|
b9c620d4cd | ||
|
|
8c9dfd9c9d | ||
|
|
8848eb8b87 |
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "cargo"
|
||||
directories:
|
||||
- "/"
|
||||
- "/examples/*"
|
||||
- "/benchmarks"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
6
.github/workflows/ci-semver.yml
vendored
6
.github/workflows/ci-semver.yml
vendored
@@ -14,8 +14,8 @@ jobs:
|
||||
|
||||
test:
|
||||
needs: [get-leptos-changed]
|
||||
if: needs.get-leptos-changed.outputs.leptos_changed == 'true' && github.event.pull_request.labels[0].name != 'breaking'
|
||||
name: Run semver check (nightly-2024-04-14)
|
||||
if: github.event.pull_request.labels[0].name == 'semver' # needs.get-leptos-changed.outputs.leptos_changed == 'true' && github.event.pull_request.labels[0].name != 'breaking'
|
||||
name: Run semver check (nightly-2024-08-01)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -25,4 +25,4 @@ jobs:
|
||||
- name: Semver Checks
|
||||
uses: obi1kenobi/cargo-semver-checks-action@v2
|
||||
with:
|
||||
rust-toolchain: nightly-2024-04-14
|
||||
rust-toolchain: nightly-2024-08-01
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -49,4 +49,4 @@ jobs:
|
||||
with:
|
||||
directory: ${{ matrix.directory }}
|
||||
cargo_make_task: "ci"
|
||||
toolchain: nightly-2024-04-14
|
||||
toolchain: nightly-2024-08-01
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
- name: Get example project directories that changed
|
||||
id: changed-dirs
|
||||
uses: tj-actions/changed-files@v41
|
||||
uses: tj-actions/changed-files@v45
|
||||
with:
|
||||
dir_names: true
|
||||
dir_names_max_depth: "2"
|
||||
|
||||
2
.github/workflows/get-example-changed.yml
vendored
2
.github/workflows/get-example-changed.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Get example files that changed
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v43
|
||||
uses: tj-actions/changed-files@v45
|
||||
with:
|
||||
files: |
|
||||
examples/**
|
||||
|
||||
2
.github/workflows/get-leptos-changed.yml
vendored
2
.github/workflows/get-leptos-changed.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Get source files that changed
|
||||
id: changed-source
|
||||
uses: tj-actions/changed-files@v43
|
||||
uses: tj-actions/changed-files@v45
|
||||
with:
|
||||
files: |
|
||||
any_error/**
|
||||
|
||||
2
.github/workflows/run-cargo-make-task.yml
vendored
2
.github/workflows/run-cargo-make-task.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
|
||||
56
Cargo.toml
56
Cargo.toml
@@ -37,37 +37,39 @@ members = [
|
||||
"router_macro",
|
||||
"any_error",
|
||||
]
|
||||
exclude = ["benchmarks", "examples"]
|
||||
exclude = ["benchmarks", "examples", "projects"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.7.0-alpha"
|
||||
rust-version = "1.75"
|
||||
version = "0.7.0-beta4"
|
||||
edition = "2021"
|
||||
rust-version = "1.76"
|
||||
|
||||
[workspace.dependencies]
|
||||
throw_error = { path = "./any_error/", version = "0.1" }
|
||||
any_spawner = { path = "./any_spawner/", version = "0.1" }
|
||||
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1" }
|
||||
either_of = { path = "./either_of/", version = "0.1" }
|
||||
hydration_context = { path = "./hydration_context", version = "0.2.0-alpha" }
|
||||
leptos = { path = "./leptos", version = "0.7.0-alpha" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.7.0-alpha" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.7.0-alpha" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0-alpha" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0-alpha" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.7.0-alpha" }
|
||||
leptos_router = { path = "./router", version = "0.7.0-alpha" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.7.0-alpha" }
|
||||
leptos_meta = { path = "./meta", version = "0.7.0-alpha" }
|
||||
next_tuple = { path = "./next_tuple", version = "0.1.0-alpha" }
|
||||
oco_ref = { path = "./oco", version = "0.2" }
|
||||
or_poisoned = { path = "./or_poisoned", version = "0.1" }
|
||||
reactive_graph = { path = "./reactive_graph", version = "0.1.0-alpha" }
|
||||
reactive_stores = { path = "./reactive_stores", version = "0.1.0-alpha" }
|
||||
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0-alpha" }
|
||||
server_fn = { path = "./server_fn", version = "0.7.0-alpha" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0-alpha" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0-alpha" }
|
||||
tachys = { path = "./tachys", version = "0.1.0-alpha" }
|
||||
throw_error = { path = "./any_error/", version = "0.2.0-beta4" }
|
||||
any_spawner = { path = "./any_spawner/", version = "0.1.0" }
|
||||
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1.0" }
|
||||
either_of = { path = "./either_of/", version = "0.1.0" }
|
||||
hydration_context = { path = "./hydration_context", version = "0.2.0-beta4" }
|
||||
leptos = { path = "./leptos", version = "0.7.0-beta4" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.7.0-beta4" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.7.0-beta4" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0-beta4" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0-beta4" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.7.0-beta4" }
|
||||
leptos_router = { path = "./router", version = "0.7.0-beta4" }
|
||||
leptos_router_macro = { path = "./router_macro", version = "0.7.0-beta4" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.7.0-beta4" }
|
||||
leptos_meta = { path = "./meta", version = "0.7.0-beta4" }
|
||||
next_tuple = { path = "./next_tuple", version = "0.1.0-beta4" }
|
||||
oco_ref = { path = "./oco", version = "0.2.0" }
|
||||
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
|
||||
reactive_graph = { path = "./reactive_graph", version = "0.1.0-beta4" }
|
||||
reactive_stores = { path = "./reactive_stores", version = "0.1.0-beta4" }
|
||||
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0-beta4" }
|
||||
server_fn = { path = "./server_fn", version = "0.7.0-beta4" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0-beta4" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0-beta4" }
|
||||
tachys = { path = "./tachys", version = "0.1.0-beta4" }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
You can find a list of useful libraries and example projects at [`awesome-leptos`](https://github.com/leptos-rs/awesome-leptos).
|
||||
|
||||
# The `main` branch is currently undergoing major changes in preparation for the [0.7](https://github.com/leptos-rs/leptos/milestone/4) release. For a stable version, please use the [v0.6.13 tag](https://github.com/leptos-rs/leptos/tree/v0.6.13)
|
||||
|
||||
# Leptos
|
||||
|
||||
```rust
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "throw_error"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0-beta4"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for wrapping, throwing, and catching errors."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pin-project-lite = "0.2"
|
||||
pin-project-lite = "0.2.14"
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
error,
|
||||
fmt::{self, Display},
|
||||
future::Future,
|
||||
ops,
|
||||
mem, ops,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
@@ -92,9 +92,25 @@ thread_local! {
|
||||
static ERROR_HOOK: RefCell<Option<Arc<dyn ErrorHook>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
/// Resets the error hook to its previous state when dropped.
|
||||
pub struct ResetErrorHookOnDrop(Option<Arc<dyn ErrorHook>>);
|
||||
|
||||
impl Drop for ResetErrorHookOnDrop {
|
||||
fn drop(&mut self) {
|
||||
ERROR_HOOK.with_borrow_mut(|this| *this = self.0.take())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current error hook.
|
||||
pub fn get_error_hook() -> Option<Arc<dyn ErrorHook>> {
|
||||
ERROR_HOOK.with_borrow(Clone::clone)
|
||||
}
|
||||
|
||||
/// Sets the current thread-local error hook, which will be invoked when [`throw`] is called.
|
||||
pub fn set_error_hook(hook: Arc<dyn ErrorHook>) {
|
||||
ERROR_HOOK.with_borrow_mut(|this| *this = Some(hook))
|
||||
pub fn set_error_hook(hook: Arc<dyn ErrorHook>) -> ResetErrorHookOnDrop {
|
||||
ResetErrorHookOnDrop(
|
||||
ERROR_HOOK.with_borrow_mut(|this| mem::replace(this, Some(hook))),
|
||||
)
|
||||
}
|
||||
|
||||
/// Invokes the error hook set by [`set_error_hook`] with the given error.
|
||||
@@ -140,9 +156,10 @@ where
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
if let Some(hook) = &this.hook {
|
||||
set_error_hook(Arc::clone(hook))
|
||||
}
|
||||
let _hook = this
|
||||
.hook
|
||||
.as_ref()
|
||||
.map(|hook| set_error_hook(Arc::clone(hook)));
|
||||
this.inner.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[package]
|
||||
name = "any_spawner"
|
||||
edition = "2021"
|
||||
version = "0.1.1"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Spawn asynchronous tasks in an executor-independent way."
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
glib = { version = "0.19", optional = true }
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", optional = true, default-features = false, features = [
|
||||
"rt",
|
||||
futures = "0.3.30"
|
||||
glib = { version = "0.20.0", optional = true }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.39", optional = true, default-features = false, features = [
|
||||
"rt",
|
||||
] }
|
||||
tracing = { version = "0.1", optional = true }
|
||||
wasm-bindgen-futures = { version = "0.4", optional = true }
|
||||
tracing = { version = "0.1.40", optional = true }
|
||||
wasm-bindgen-futures = { version = "0.4.42", optional = true }
|
||||
|
||||
[features]
|
||||
tracing = ["dep:tracing"]
|
||||
@@ -28,3 +28,6 @@ futures-executor = ["futures/thread-pool", "futures/executor"]
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["tracing"]
|
||||
|
||||
@@ -6,31 +6,31 @@ rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
l0410 = { package = "leptos", version = "0.4.10", features = [
|
||||
"nightly",
|
||||
"ssr",
|
||||
"nightly",
|
||||
"ssr",
|
||||
] }
|
||||
leptos = { path = "../leptos", features = ["ssr", "nightly"] }
|
||||
leptos_reactive = { path = "../leptos_reactive", features = ["ssr", "nightly"] }
|
||||
tachydom = { git = "https://github.com/gbj/tachys", features = [
|
||||
"nightly",
|
||||
"leptos",
|
||||
"nightly",
|
||||
"leptos",
|
||||
] }
|
||||
tachy_maccy = { git = "https://github.com/gbj/tachys", features = ["nightly"] }
|
||||
sycamore = { version = "0.8", features = ["ssr"] }
|
||||
yew = { version = "0.20", features = ["ssr"] }
|
||||
tokio-test = "0.4"
|
||||
miniserde = "0.1"
|
||||
gloo = "0.8"
|
||||
uuid = { version = "1", features = ["serde", "v4", "wasm-bindgen"] }
|
||||
wasm-bindgen = "0.2"
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
serde = { version = "1", features = ["derive", "rc"] }
|
||||
serde_json = "1"
|
||||
tera = "1"
|
||||
sycamore = { version = "0.8.0", features = ["ssr"] }
|
||||
yew = { version = "0.20.0", features = ["ssr"] }
|
||||
tokio-test = "0.4.0"
|
||||
miniserde = "0.1.0"
|
||||
gloo = "0.8.0"
|
||||
uuid = { version = "1.0", features = ["serde", "v4", "wasm-bindgen"] }
|
||||
wasm-bindgen = "0.2.0"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4.0"
|
||||
strum = "0.24.0"
|
||||
strum_macros = "0.24.0"
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_json = "1.0"
|
||||
tera = "1.0"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
version = "0.3.0"
|
||||
features = ["Window", "Document", "HtmlElement", "HtmlInputElement"]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
[package]
|
||||
name = "const_str_slice_concat"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
@@ -8,5 +7,6 @@ readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for const concatenation of string slices."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
[package]
|
||||
name = "either_of"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
@@ -8,6 +7,7 @@ readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for working with enumerated types that contain one of 2..n other types."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pin-project-lite = "0.2"
|
||||
pin-project-lite = "0.2.14"
|
||||
|
||||
@@ -7,19 +7,18 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
cfg-if = "1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
cfg-if = "1.0"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
wasm-bindgen = "0.2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
|
||||
@@ -1,68 +1,9 @@
|
||||
<picture>
|
||||
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
|
||||
</picture>
|
||||
# Action Form Error Handling Example
|
||||
|
||||
# Leptos Starter Template
|
||||
## Getting Started
|
||||
|
||||
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
|
||||
See the [Examples README](../README.md) for setup and run instructions.
|
||||
|
||||
## Creating your template repo
|
||||
## Quick Start
|
||||
|
||||
If you don't have `cargo-leptos` installed you can install it with
|
||||
|
||||
`cargo install cargo-leptos`
|
||||
|
||||
Then run
|
||||
|
||||
`cargo leptos new --git leptos-rs/start`
|
||||
|
||||
to generate a new project template (you will be prompted to enter a project name).
|
||||
|
||||
`cd {projectname}`
|
||||
|
||||
to go to your newly created project.
|
||||
|
||||
Of course, you should explore around the project structure, but the best place to start with your application code is in `src/app.rs`.
|
||||
|
||||
## Running your project
|
||||
|
||||
`cargo leptos watch`
|
||||
By default, you can access your local project at `http://localhost:3000`
|
||||
|
||||
## Installing Additional Tools
|
||||
|
||||
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||
|
||||
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||
2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||
3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||
4. `npm install -g sass` - install `dart-sass` (should be optional in future)
|
||||
|
||||
## Executing a Server on a Remote Machine Without the Toolchain
|
||||
After running a `cargo leptos build --release` the minimum files needed are:
|
||||
|
||||
1. The server binary located in `target/server/release`
|
||||
2. The `site` directory and all files within located in `target/site`
|
||||
|
||||
Copy these files to your remote server. The directory structure should be:
|
||||
```text
|
||||
leptos_start
|
||||
site/
|
||||
```
|
||||
Set the following environment variables (updating for your project as needed):
|
||||
```sh
|
||||
export LEPTOS_OUTPUT_NAME="leptos_start"
|
||||
export LEPTOS_SITE_ROOT="site"
|
||||
export LEPTOS_SITE_PKG_DIR="pkg"
|
||||
export LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
||||
export LEPTOS_RELOAD_PORT="3001"
|
||||
```
|
||||
Finally, run the server binary.
|
||||
|
||||
## Notes about CSR and Trunk:
|
||||
Although it is not recommended, you can also run your project without server integration using the feature `csr` and `trunk serve`:
|
||||
|
||||
`trunk serve --open --features csr`
|
||||
|
||||
This may be useful for integrating external tools which require a static site, e.g. `tauri`.
|
||||
Execute `cargo leptos watch` to run this example.
|
||||
|
||||
@@ -52,23 +52,10 @@ async fn main() -> std::io::Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "ssr", feature = "csr")))]
|
||||
#[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
|
||||
// see optional feature `csr` instead
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "ssr"), feature = "csr"))]
|
||||
pub fn main() {
|
||||
// a client-side main function is required for using `trunk serve`
|
||||
// prefer using `cargo leptos serve` instead
|
||||
// to run: `trunk serve --open --features csr`
|
||||
use action_form_error_handling::app::*;
|
||||
use leptos::prelude::*;
|
||||
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
mount_to_body(App);
|
||||
}
|
||||
|
||||
111
examples/axum_js_ssr/Cargo.toml
Normal file
111
examples/axum_js_ssr/Cargo.toml
Normal file
@@ -0,0 +1,111 @@
|
||||
[package]
|
||||
name = "axum_js_ssr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
gloo-utils = "0.2.0"
|
||||
html-escape = "0.2.13"
|
||||
http-body-util = { version = "0.1.0", optional = true }
|
||||
js-sys = { version = "0.3.69", optional = true }
|
||||
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 }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
wasm-bindgen = "0.2.92"
|
||||
web-sys = { version = "0.3.69", features = [ "AddEventListenerOptions", "Document", "Element", "Event", "EventListener", "EventTarget", "Performance", "Window" ], optional = true }
|
||||
|
||||
[features]
|
||||
hydrate = [
|
||||
"leptos/hydrate",
|
||||
"dep:js-sys",
|
||||
"dep:web-sys",
|
||||
]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:http-body-util",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"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 = "axum_js_ssr"
|
||||
# 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"
|
||||
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
|
||||
style-file = "style/main.scss"
|
||||
# Assets source dir. All files found here will be copied and synchronized to site-root.
|
||||
# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.
|
||||
#
|
||||
# Optional. Env: LEPTOS_ASSETS_DIR.
|
||||
assets-dir = "assets"
|
||||
# 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 = "npx playwright test"
|
||||
end2end-dir = "end2end"
|
||||
# The browserlist query used for optimizing the CSS.
|
||||
browserquery = "defaults"
|
||||
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
|
||||
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
|
||||
|
||||
lib-profile-release = "wasm-release"
|
||||
21
examples/axum_js_ssr/LICENSE
Normal file
21
examples/axum_js_ssr/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Tommy Yu
|
||||
|
||||
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.
|
||||
8
examples/axum_js_ssr/Makefile.toml
Normal file
8
examples/axum_js_ssr/Makefile.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
extend = [
|
||||
{ path = "../cargo-make/main.toml" },
|
||||
{ path = "../cargo-make/cargo-leptos.toml" },
|
||||
]
|
||||
|
||||
[env]
|
||||
|
||||
CLIENT_PROCESS_NAME = "axum_js_ssr"
|
||||
10
examples/axum_js_ssr/README.md
Normal file
10
examples/axum_js_ssr/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Leptos Axum JS SSR Example
|
||||
|
||||
This example shows the various ways that JavaScript may be included into
|
||||
a Leptos application. The intent is to demonstrate how this may be done
|
||||
and how it may cause the application to fail in an unexpected manner if
|
||||
done incorrectly.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run `cargo leptos watch` to run this example.
|
||||
BIN
examples/axum_js_ssr/assets/favicon.ico
Normal file
BIN
examples/axum_js_ssr/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
29
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/LICENSE
generated
vendored
Normal file
29
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2006, Ivan Sagalaev.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
47
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/README.md
generated
vendored
Normal file
47
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/README.md
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# Highlight.js CDN Assets
|
||||
|
||||
**Note: this contains only a subset of files from the full package from NPM.**
|
||||
|
||||
[](https://packagephobia.now.sh/result?p=highlight.js)
|
||||
|
||||
**This package contains only the CDN build assets of highlight.js.**
|
||||
|
||||
This may be what you want if you'd like to install the pre-built distributable highlight.js client-side assets via NPM. If you're wanting to use highlight.js mainly on the server-side you likely want the [highlight.js][1] package instead.
|
||||
|
||||
To access these files via CDN:<br>
|
||||
https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/
|
||||
|
||||
**If you just want a single .js file with the common languages built-in:
|
||||
<https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/highlight.min.js>**
|
||||
|
||||
---
|
||||
|
||||
## Highlight.js
|
||||
|
||||
Highlight.js is a syntax highlighter written in JavaScript. It works in
|
||||
the browser as well as on the server. It works with pretty much any
|
||||
markup, doesn’t depend on any framework, and has automatic language
|
||||
detection.
|
||||
|
||||
If you'd like to read the full README:<br>
|
||||
<https://github.com/highlightjs/highlight.js/blob/main/README.md>
|
||||
|
||||
## License
|
||||
|
||||
Highlight.js is released under the BSD License. See [LICENSE][7] file
|
||||
for details.
|
||||
|
||||
## Links
|
||||
|
||||
The official site for the library is at <https://highlightjs.org/>.
|
||||
|
||||
The Github project may be found at: <https://github.com/highlightjs/highlight.js>
|
||||
|
||||
Further in-depth documentation for the API and other topics is at
|
||||
<http://highlightjs.readthedocs.io/>.
|
||||
|
||||
A list of the Core Team and contributors can be found in the [CONTRIBUTORS.md][8] file.
|
||||
|
||||
[1]: https://www.npmjs.com/package/highlight.js
|
||||
[7]: https://github.com/highlightjs/highlight.js/blob/main/LICENSE
|
||||
[8]: https://github.com/highlightjs/highlight.js/blob/main/CONTRIBUTORS.md
|
||||
1230
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/es/highlight.min.js
generated
vendored
Normal file
1230
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/es/highlight.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1232
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/highlight.min.js
generated
vendored
Normal file
1232
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/highlight.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
93
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/package.json
generated
vendored
Normal file
93
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/package.json
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"name": "@highlightjs/cdn-assets",
|
||||
"description": "Syntax highlighting with language autodetection. (pre-compiled CDN assets)",
|
||||
"keywords": [
|
||||
"highlight",
|
||||
"syntax"
|
||||
],
|
||||
"homepage": "https://highlightjs.org/",
|
||||
"version": "11.10.0",
|
||||
"author": "Josh Goebel <hello@joshgoebel.com>",
|
||||
"contributors": [
|
||||
"Josh Goebel <hello@joshgoebel.com>",
|
||||
"Egor Rogov <e.rogov@postgrespro.ru>",
|
||||
"Vladimir Jimenez <me@allejo.io>",
|
||||
"Ivan Sagalaev <maniac@softwaremaniacs.org>",
|
||||
"Jeremy Hull <sourdrums@gmail.com>",
|
||||
"Oleg Efimov <efimovov@gmail.com>",
|
||||
"Gidi Meir Morris <gidi@gidi.io>",
|
||||
"Jan T. Sott <git@idleberg.com>",
|
||||
"Li Xuanji <xuanji@gmail.com>",
|
||||
"Marcos Cáceres <marcos@marcosc.com>",
|
||||
"Sang Dang <sang.dang@polku.io>"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/highlightjs/highlight.js/issues"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/highlightjs/highlight.js.git"
|
||||
},
|
||||
"sideEffects": [
|
||||
"./es/common.js",
|
||||
"./lib/common.js",
|
||||
"*.css",
|
||||
"*.scss"
|
||||
],
|
||||
"scripts": {
|
||||
"mocha": "mocha",
|
||||
"lint": "eslint src/*.js src/lib/*.js demo/*.js tools/**/*.js --ignore-pattern vendor",
|
||||
"lint-languages": "eslint --no-eslintrc -c .eslintrc.lang.js src/languages/**/*.js",
|
||||
"build_and_test": "npm run build && npm run test",
|
||||
"build_and_test_browser": "npm run build-browser && npm run test-browser",
|
||||
"build": "node ./tools/build.js -t node",
|
||||
"build-cdn": "node ./tools/build.js -t cdn",
|
||||
"build-browser": "node ./tools/build.js -t browser :common",
|
||||
"devtool": "npx http-server",
|
||||
"test": "mocha test",
|
||||
"test-markup": "mocha test/markup",
|
||||
"test-detect": "mocha test/detect",
|
||||
"test-browser": "mocha test/browser",
|
||||
"test-parser": "mocha test/parser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@colors/colors": "^1.6.0",
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-json": "^6.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/mocha": "^10.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
"clean-css": "^5.3.2",
|
||||
"cli-table": "^0.3.1",
|
||||
"commander": "^12.1.0",
|
||||
"css": "^3.0.0",
|
||||
"css-color-names": "^1.0.1",
|
||||
"deep-freeze-es6": "^3.0.2",
|
||||
"del": "^7.1.0",
|
||||
"dependency-resolver": "^2.0.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"glob": "^8.1.0",
|
||||
"glob-promise": "^6.0.5",
|
||||
"handlebars": "^4.7.8",
|
||||
"http-server": "^14.1.1",
|
||||
"jsdom": "^24.1.0",
|
||||
"lodash": "^4.17.20",
|
||||
"mocha": "^10.2.0",
|
||||
"refa": "^0.4.1",
|
||||
"rollup": "^4.0.2",
|
||||
"should": "^13.2.3",
|
||||
"terser": "^5.21.0",
|
||||
"tiny-worker": "^2.3.0",
|
||||
"typescript": "^5.2.2",
|
||||
"wcag-contrast": "^3.0.0"
|
||||
}
|
||||
}
|
||||
10
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/styles/github-dark.min.css
generated
vendored
Normal file
10
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/styles/github-dark.min.css
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: GitHub Dark
|
||||
Description: Dark theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Outdated base version: https://github.com/primer/github-syntax-dark
|
||||
Current colors taken from GitHub's CSS
|
||||
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
|
||||
10
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/styles/github.min.css
generated
vendored
Normal file
10
examples/axum_js_ssr/node_modules/@highlightjs/cdn-assets/styles/github.min.css
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: GitHub
|
||||
Description: Light theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Outdated base version: https://github.com/primer/github-syntax-light
|
||||
Current colors taken from GitHub's CSS
|
||||
*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
|
||||
6
examples/axum_js_ssr/package.json
Normal file
6
examples/axum_js_ssr/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "axum_js_ssr",
|
||||
"dependencies": {
|
||||
"@highlightjs/cdn-assets": "^11.10.0"
|
||||
}
|
||||
}
|
||||
2
examples/axum_js_ssr/rust-toolchain.toml
Normal file
2
examples/axum_js_ssr/rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "stable" # test change
|
||||
8
examples/axum_js_ssr/src/api.rs
Normal file
8
examples/axum_js_ssr/src/api.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use leptos::{prelude::ServerFnError, server};
|
||||
|
||||
#[server]
|
||||
pub async fn fetch_code() -> Result<String, ServerFnError> {
|
||||
// emulate loading of code from a database/version control/etc
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
Ok(crate::consts::CH05_02A.to_string())
|
||||
}
|
||||
1134
examples/axum_js_ssr/src/app.rs
Normal file
1134
examples/axum_js_ssr/src/app.rs
Normal file
File diff suppressed because it is too large
Load Diff
39
examples/axum_js_ssr/src/consts.rs
Normal file
39
examples/axum_js_ssr/src/consts.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Example programs from the Rust Programming Language Book
|
||||
|
||||
pub const CH03_05A: &str = r#"fn main() {
|
||||
let number = 3;
|
||||
|
||||
if number < 5 {
|
||||
println!("condition was true");
|
||||
} else {
|
||||
println!("condition was false");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
"#;
|
||||
|
||||
// For some reason, swapping the code examples "fixes" example 6. It
|
||||
// might have something to do with the lower complexity of highlighting
|
||||
// a shorter example. Anyway, including extra newlines for the shorter
|
||||
// example to match with the longer in order to avoid reflowing the
|
||||
// table during the async resource loading for CSR.
|
||||
|
||||
pub const CH05_02A: &str = r#"fn main() {
|
||||
let width1 = 30;
|
||||
let height1 = 50;
|
||||
|
||||
println!(
|
||||
"The area of the rectangle is {} square pixels.",
|
||||
area(width1, height1)
|
||||
);
|
||||
}
|
||||
|
||||
fn area(width: u32, height: u32) -> u32 {
|
||||
width * height
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const LEPTOS_HYDRATED: &str = "_leptos_hydrated";
|
||||
59
examples/axum_js_ssr/src/hljs.rs
Normal file
59
examples/axum_js_ssr/src/hljs.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
mod csr {
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use js_sys::{
|
||||
Object,
|
||||
Reflect::{get, set},
|
||||
};
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||
|
||||
#[wasm_bindgen(
|
||||
module = "/node_modules/@highlightjs/cdn-assets/es/highlight.min.js"
|
||||
)]
|
||||
extern "C" {
|
||||
type HighlightOptions;
|
||||
|
||||
#[wasm_bindgen(catch, js_namespace = default, js_name = highlight)]
|
||||
fn highlight_lang(
|
||||
code: String,
|
||||
options: Object,
|
||||
) -> Result<Object, JsValue>;
|
||||
|
||||
#[wasm_bindgen(js_namespace = default, js_name = highlightAll)]
|
||||
pub fn highlight_all();
|
||||
}
|
||||
|
||||
// Keeping the `ignoreIllegals` argument out of the default case, and since there is no optional arguments
|
||||
// in Rust, this will have to be provided in a separate function (e.g. `highlight_ignore_illegals`), much
|
||||
// like how `web_sys` does it for the browser APIs. For simplicity, only the highlighted HTML code is
|
||||
// returned on success, and None on error.
|
||||
pub fn highlight(code: String, lang: String) -> Option<String> {
|
||||
let options = js_sys::Object::new();
|
||||
set(&options, &"language".into(), &lang.into())
|
||||
.expect("failed to assign lang to options");
|
||||
highlight_lang(code, options)
|
||||
.map(|result| {
|
||||
let value = get(&result, &"value".into())
|
||||
.expect("HighlightResult failed to contain the value key");
|
||||
value.into_serde().expect("Value should have been a string")
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
mod ssr {
|
||||
// noop under ssr
|
||||
pub fn highlight_all() {}
|
||||
|
||||
// TODO see if there is a Rust-based solution that will enable isomorphic rendering for this feature.
|
||||
// the current (disabled) implementation simply calls html_escape.
|
||||
// pub fn highlight(code: String, _lang: String) -> Option<String> {
|
||||
// Some(html_escape::encode_text(&code).into_owned())
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub use csr::*;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub use ssr::*;
|
||||
51
examples/axum_js_ssr/src/lib.rs
Normal file
51
examples/axum_js_ssr/src/lib.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
pub mod api;
|
||||
pub mod app;
|
||||
pub mod consts;
|
||||
pub mod hljs;
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||
pub fn hydrate() {
|
||||
use app::*;
|
||||
use consts::LEPTOS_HYDRATED;
|
||||
use std::panic;
|
||||
panic::set_hook(Box::new(|info| {
|
||||
// this custom hook will call out to show the usual error log at
|
||||
// the console while also attempt to update the UI to indicate
|
||||
// a restart of the application is required to continue.
|
||||
console_error_panic_hook::hook(info);
|
||||
let window = leptos::prelude::window();
|
||||
if !matches!(
|
||||
js_sys::Reflect::get(&window, &wasm_bindgen::JsValue::from_str(LEPTOS_HYDRATED)),
|
||||
Ok(t) if t == true
|
||||
) {
|
||||
let document = leptos::prelude::document();
|
||||
let _ = document.query_selector("#reset").map(|el| {
|
||||
el.map(|el| {
|
||||
el.set_class_name("panicked");
|
||||
})
|
||||
});
|
||||
let _ = document.query_selector("#notice").map(|el| {
|
||||
el.map(|el| {
|
||||
el.set_class_name("panicked");
|
||||
})
|
||||
});
|
||||
}
|
||||
}));
|
||||
leptos::mount::hydrate_body(App);
|
||||
|
||||
let window = leptos::prelude::window();
|
||||
js_sys::Reflect::set(
|
||||
&window,
|
||||
&wasm_bindgen::JsValue::from_str(LEPTOS_HYDRATED),
|
||||
&wasm_bindgen::JsValue::TRUE,
|
||||
)
|
||||
.expect("error setting hydrated status");
|
||||
let event = web_sys::Event::new(LEPTOS_HYDRATED)
|
||||
.expect("error creating hydrated event");
|
||||
let document = leptos::prelude::document();
|
||||
document
|
||||
.dispatch_event(&event)
|
||||
.expect("error dispatching hydrated event");
|
||||
leptos::logging::log!("dispatched hydrated event");
|
||||
}
|
||||
152
examples/axum_js_ssr/src/main.rs
Normal file
152
examples/axum_js_ssr/src/main.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
#[cfg(feature = "ssr")]
|
||||
mod latency {
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
pub static LATENCY: OnceLock<
|
||||
Mutex<std::iter::Cycle<std::slice::Iter<'_, u64>>>,
|
||||
> = OnceLock::new();
|
||||
pub static ES_LATENCY: OnceLock<
|
||||
Mutex<std::iter::Cycle<std::slice::Iter<'_, u64>>>,
|
||||
> = OnceLock::new();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::Request,
|
||||
http::{
|
||||
header::{self, HeaderValue},
|
||||
StatusCode,
|
||||
},
|
||||
middleware::{self, Next},
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_js_ssr::app::*;
|
||||
use http_body_util::BodyExt;
|
||||
use leptos::prelude::*;
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
|
||||
latency::LATENCY.get_or_init(|| [0, 4, 40, 400].iter().cycle().into());
|
||||
latency::ES_LATENCY.get_or_init(|| [0].iter().cycle().into());
|
||||
// Having the ES_LATENCY (a cycle of latency for the loading of the es
|
||||
// module) in an identical cycle as LATENCY (for the standard version)
|
||||
// adversely influences the intended demo, as this ultimately delays
|
||||
// hydration when set too high which can cause panic under every case.
|
||||
// If you want to test the effects of the delay just modify the list of
|
||||
// values for the desired cycle of delays.
|
||||
|
||||
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);
|
||||
|
||||
async fn highlight_js() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "text/javascript")],
|
||||
include_str!(
|
||||
"../node_modules/@highlightjs/cdn-assets/highlight.min.js"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
async fn latency_for_highlight_js(
|
||||
req: Request,
|
||||
next: Next,
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||
let uri_parts = &mut req.uri().path().rsplit('/');
|
||||
|
||||
let is_highlightjs = uri_parts.next() == Some("highlight.min.js");
|
||||
let es = uri_parts.next() == Some("es");
|
||||
let module_type = if es { "es module " } else { "standard " };
|
||||
let res = next.run(req).await;
|
||||
if is_highlightjs {
|
||||
// additional processing if the filename is the test subject
|
||||
let (mut parts, body) = res.into_parts();
|
||||
let bytes = body
|
||||
.collect()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
(
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("error reading body: {err}"),
|
||||
)
|
||||
})?
|
||||
.to_bytes();
|
||||
let latency = if es {
|
||||
&latency::ES_LATENCY
|
||||
} else {
|
||||
&latency::LATENCY
|
||||
};
|
||||
|
||||
let delay = match latency
|
||||
.get()
|
||||
.expect("latency cycle wasn't set up")
|
||||
.try_lock()
|
||||
{
|
||||
Ok(ref mut mutex) => {
|
||||
*mutex.next().expect("cycle always has next")
|
||||
}
|
||||
Err(_) => 0,
|
||||
};
|
||||
|
||||
// inject the logging of the delay used into the target script
|
||||
log!(
|
||||
"loading {module_type}highlight.min.js with latency of \
|
||||
{delay} ms"
|
||||
);
|
||||
let js_log = format!(
|
||||
"\nconsole.log('loaded {module_type}highlight.js with a \
|
||||
minimum latency of {delay} ms');"
|
||||
);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(delay)).await;
|
||||
|
||||
let bytes = [bytes, js_log.into()].concat();
|
||||
let length = bytes.len();
|
||||
let body = Body::from(bytes);
|
||||
|
||||
// Provide the bare minimum set of headers to avoid browser cache.
|
||||
parts.headers = header::HeaderMap::from_iter(
|
||||
[
|
||||
(
|
||||
header::CONTENT_TYPE,
|
||||
HeaderValue::from_static("text/javascript"),
|
||||
),
|
||||
(header::CONTENT_LENGTH, HeaderValue::from(length)),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
Ok(Response::from_parts(parts, body))
|
||||
} else {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
let app = Router::new()
|
||||
.route("/highlight.min.js", get(highlight_js))
|
||||
.leptos_routes(&leptos_options, routes, {
|
||||
let leptos_options = leptos_options.clone();
|
||||
move || shell(leptos_options.clone())
|
||||
})
|
||||
.fallback(leptos_axum::file_and_error_handler(shell))
|
||||
.layer(middleware::from_fn(latency_for_highlight_js))
|
||||
.with_state(leptos_options);
|
||||
|
||||
// run our app with hyper
|
||||
// `axum::Server` is a re-export of `hyper::Server`
|
||||
log!("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
|
||||
}
|
||||
171
examples/axum_js_ssr/style/main.scss
Normal file
171
examples/axum_js_ssr/style/main.scss
Normal file
@@ -0,0 +1,171 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
min-width: 17em;
|
||||
height: 100vh;
|
||||
counter-reset: example-counter 0;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
padding: 0.5em 2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
nav a small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
nav a.example::before {
|
||||
counter-reset: subexample-counter 0;
|
||||
counter-increment: example-counter 1;
|
||||
content: counter(example-counter) ". ";
|
||||
}
|
||||
|
||||
nav a.subexample::before {
|
||||
counter-increment: subexample-counter 1;
|
||||
content: counter(example-counter) "." counter(subexample-counter) " ";
|
||||
}
|
||||
|
||||
div#notice {
|
||||
display: none;
|
||||
}
|
||||
|
||||
main div#notice.panicked {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 0.5em 2em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
main article {
|
||||
max-width: 60em;
|
||||
margin: 0 1em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
main p, main li {
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
main li pre code, main div pre code {
|
||||
display: block;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
main ol, main ul {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
h2>code, p>code, li>code {
|
||||
border-radius: 3px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
li pre code, div pre code {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#code-demo {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#code-demo table {
|
||||
width: 50em;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#code-demo table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#code-demo table code {
|
||||
display: block;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
nav {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
nav a[aria-current="page"] {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
nav a:hover, h2>code, p>code, li>code {
|
||||
background-color: #e7e7e7;
|
||||
}
|
||||
|
||||
nav a.panicked, main div#notice.panicked {
|
||||
background: #fdd;
|
||||
}
|
||||
|
||||
main div#notice.panicked a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
nav a.section {
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
nav {
|
||||
background: #080808;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
nav a[aria-current="page"] {
|
||||
background-color: #3f3f3f;
|
||||
}
|
||||
|
||||
nav a:hover, h2>code, p>code, li>code {
|
||||
background-color: #383838;
|
||||
}
|
||||
|
||||
nav a.panicked, main div#notice.panicked {
|
||||
background: #733;
|
||||
}
|
||||
|
||||
main div#notice.panicked a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
nav a.section {
|
||||
border-bottom: 1px solid #888;
|
||||
}
|
||||
}
|
||||
|
||||
// Just include the raw style as-is because I can't find a quick and easy way to import them just for the
|
||||
// appropriate media type...
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}
|
||||
@media (prefers-color-scheme: light){.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}}
|
||||
@media (prefers-color-scheme: dark){.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}}
|
||||
@@ -10,12 +10,12 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
gloo-timers = { version = "0.3.0", features = ["futures"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -11,34 +11,34 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
broadcaster = "1"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
lazy_static = "1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
broadcaster = "1.0"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
lazy_static = "1.5"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
once_cell = "1.18"
|
||||
gloo-net = { git = "https://github.com/rustwasm/gloo" }
|
||||
wasm-bindgen = "0.2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
simple_logger = "4.3"
|
||||
tracing = { version = "0.1", optional = true }
|
||||
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"] }
|
||||
simple_logger = "5.0"
|
||||
tracing = { version = "0.1.40", optional = true }
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:tracing",
|
||||
"leptos/ssr",
|
||||
"leptos_actix",
|
||||
"leptos_router/ssr",
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:tracing",
|
||||
"leptos/ssr",
|
||||
"leptos_actix",
|
||||
"leptos_router/ssr",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -113,10 +113,10 @@ pub fn Counter() -> impl IntoView {
|
||||
</p>
|
||||
<ErrorBoundary fallback=|errors| move || format!("Error: {:#?}", errors.get())>
|
||||
<div>
|
||||
<button on:click=move |_| clear.dispatch(())>"Clear"</button>
|
||||
<button on:click=move |_| dec.dispatch(())>"-1"</button>
|
||||
<button on:click=move |_| { clear.dispatch(()); }>"Clear"</button>
|
||||
<button on:click=move |_| { dec.dispatch(()); }>"-1"</button>
|
||||
<span>"Value: " <Suspense>{counter} "!"</Suspense></span>
|
||||
<button on:click=move |_| inc.dispatch(())>"+1"</button>
|
||||
<button on:click=move |_| { inc.dispatch(()); }>"+1"</button>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
@@ -224,12 +224,12 @@ pub fn MultiuserCounter() -> impl IntoView {
|
||||
"This one uses server-sent events (SSE) to live-update when other users make changes."
|
||||
</p>
|
||||
<div>
|
||||
<button on:click=move |_| clear.dispatch(())>"Clear"</button>
|
||||
<button on:click=move |_| dec.dispatch(())>"-1"</button>
|
||||
<button on:click=move |_| { clear.dispatch(()); }>"Clear"</button>
|
||||
<button on:click=move |_| { dec.dispatch(()); }>"-1"</button>
|
||||
<span>
|
||||
"Multiplayer Value: " {move || multiplayer_value.get().unwrap_or_default()}
|
||||
</span>
|
||||
<button on:click=move |_| inc.dispatch(())>"+1"</button>
|
||||
<button on:click=move |_| { inc.dispatch(()); }>"+1"</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ leptos_router = { path = "../../router", features = [] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -14,10 +14,10 @@ console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.34"
|
||||
pretty_assertions = "1.3.0"
|
||||
rstest = "0.17.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
pretty_assertions = "1.4"
|
||||
rstest = "0.22.0"
|
||||
|
||||
[dev-dependencies.web-sys]
|
||||
features = ["HtmlElement", "XPathResult"]
|
||||
version = "0.3.61"
|
||||
version = "0.3.70"
|
||||
|
||||
6
examples/counters/.gitignore
vendored
Normal file
6
examples/counters/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Support playwright testing
|
||||
node_modules/
|
||||
test-results/
|
||||
end2end/playwright-report/
|
||||
playwright/.cache/
|
||||
pnpm-lock.yaml
|
||||
@@ -4,10 +4,10 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -2,4 +2,5 @@ extend = [
|
||||
{ path = "../cargo-make/main.toml" },
|
||||
{ path = "../cargo-make/wasm-test.toml" },
|
||||
{ path = "../cargo-make/trunk_server.toml" },
|
||||
{ path = "../cargo-make/playwright-trunk-test.toml" },
|
||||
]
|
||||
|
||||
4
examples/counters/e2e/.gitignore
vendored
Normal file
4
examples/counters/e2e/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
83
examples/counters/e2e/package-lock.json
generated
Normal file
83
examples/counters/e2e/package-lock.json
generated
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "grip",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "grip",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.35.1"
|
||||
}
|
||||
},
|
||||
"node_modules/.pnpm/@playwright+test@1.33.0": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/.pnpm/@types+node@20.2.1/node_modules/@types/node": {
|
||||
"version": "20.2.1",
|
||||
"extraneous": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/.pnpm/playwright-core@1.33.0/node_modules/playwright-core": {
|
||||
"version": "1.33.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz",
|
||||
"integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.35.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
|
||||
"integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.35.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz",
|
||||
"integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
examples/counters/e2e/package.json
Normal file
10
examples/counters/e2e/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"private": "true",
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.46.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"pnpm": "^9.7.1"
|
||||
}
|
||||
}
|
||||
77
examples/counters/e2e/playwright.config.ts
Normal file
77
examples/counters/e2e/playwright.config.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !process.env.DEV,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.DEV ? 0 : 10,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.DEV ? 1 : 1,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [["html", { open: "never" }], ["list"]],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: "http://127.0.0.1:8080",
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: "firefox",
|
||||
// use: { ...devices["Desktop Firefox"] },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: "webkit",
|
||||
// use: { ...devices["Desktop Safari"] },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: "cd ../ && trunk serve",
|
||||
// url: "http://127.0.0.1:8080",
|
||||
// reuseExistingServer: false, //!process.env.CI,
|
||||
// },
|
||||
});
|
||||
19
examples/counters/e2e/tests/add_1k_counters.spec.ts
Normal file
19
examples/counters/e2e/tests/add_1k_counters.spec.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Add 1000 Counters", () => {
|
||||
test("should increase the number of counters", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
|
||||
await Promise.all([
|
||||
await ui.goto(),
|
||||
await ui.addOneThousandCountersButton.waitFor(),
|
||||
]);
|
||||
|
||||
await ui.addOneThousandCounters();
|
||||
await ui.addOneThousandCounters();
|
||||
await ui.addOneThousandCounters();
|
||||
|
||||
await expect(ui.counters).toHaveText("3000");
|
||||
});
|
||||
});
|
||||
15
examples/counters/e2e/tests/add_counter.spec.ts
Normal file
15
examples/counters/e2e/tests/add_counter.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Add Counter", () => {
|
||||
test("should increase the number of counters", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
|
||||
await expect(ui.counters).toHaveText("3");
|
||||
});
|
||||
});
|
||||
18
examples/counters/e2e/tests/clear_counters.spec.ts
Normal file
18
examples/counters/e2e/tests/clear_counters.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Clear Counters", () => {
|
||||
test("should reset the counts", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.clearCounters();
|
||||
|
||||
await expect(ui.total).toHaveText("0");
|
||||
await expect(ui.counters).toHaveText("0");
|
||||
});
|
||||
});
|
||||
16
examples/counters/e2e/tests/decrement_count.spec.ts
Normal file
16
examples/counters/e2e/tests/decrement_count.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Decrement Count", () => {
|
||||
test("should decrease the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.decrementCount();
|
||||
await ui.decrementCount();
|
||||
await ui.decrementCount();
|
||||
|
||||
await expect(ui.total).toHaveText("-3");
|
||||
});
|
||||
});
|
||||
30
examples/counters/e2e/tests/enter_count.spec.ts
Normal file
30
examples/counters/e2e/tests/enter_count.spec.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Enter Count", () => {
|
||||
test("should increase the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.enterCount("5");
|
||||
|
||||
await expect(ui.total).toHaveText("5");
|
||||
await expect(ui.counters).toHaveText("1");
|
||||
});
|
||||
|
||||
test("should decrease the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.enterCount("100");
|
||||
await ui.enterCount("100", 1);
|
||||
await ui.enterCount("100", 2);
|
||||
await ui.enterCount("50", 1);
|
||||
|
||||
await expect(ui.total).toHaveText("250");
|
||||
});
|
||||
});
|
||||
98
examples/counters/e2e/tests/fixtures/counters_page.ts
vendored
Normal file
98
examples/counters/e2e/tests/fixtures/counters_page.ts
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
import { expect, Locator, Page } from "@playwright/test";
|
||||
|
||||
export class CountersPage {
|
||||
readonly page: Page;
|
||||
readonly addCounterButton: Locator;
|
||||
readonly addOneThousandCountersButton: Locator;
|
||||
readonly clearCountersButton: Locator;
|
||||
|
||||
readonly incrementCountButton: Locator;
|
||||
readonly counterInput: Locator;
|
||||
readonly decrementCountButton: Locator;
|
||||
readonly removeCountButton: Locator;
|
||||
|
||||
readonly total: Locator;
|
||||
readonly counters: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
|
||||
this.addCounterButton = page.locator("button", { hasText: "Add Counter" });
|
||||
|
||||
this.addOneThousandCountersButton = page.locator("button", {
|
||||
hasText: "Add 1000 Counters",
|
||||
});
|
||||
|
||||
this.clearCountersButton = page.locator("button", {
|
||||
hasText: "Clear Counters",
|
||||
});
|
||||
|
||||
this.decrementCountButton = page.locator("button", {
|
||||
hasText: "-1",
|
||||
});
|
||||
|
||||
this.incrementCountButton = page.locator("button", {
|
||||
hasText: "+1",
|
||||
});
|
||||
|
||||
this.removeCountButton = page.locator("button", {
|
||||
hasText: "x",
|
||||
});
|
||||
|
||||
this.total = page.getByTestId("total");
|
||||
|
||||
this.counters = page.getByTestId("counters");
|
||||
|
||||
this.counterInput = page.getByRole("textbox");
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto("/");
|
||||
}
|
||||
|
||||
async addCounter() {
|
||||
await Promise.all([
|
||||
this.addCounterButton.waitFor(),
|
||||
this.addCounterButton.click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async addOneThousandCounters() {
|
||||
this.addOneThousandCountersButton.click();
|
||||
}
|
||||
|
||||
async decrementCount(index: number = 0) {
|
||||
await Promise.all([
|
||||
this.decrementCountButton.nth(index).waitFor(),
|
||||
this.decrementCountButton.nth(index).click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async incrementCount(index: number = 0) {
|
||||
await Promise.all([
|
||||
this.incrementCountButton.nth(index).waitFor(),
|
||||
this.incrementCountButton.nth(index).click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async clearCounters() {
|
||||
await Promise.all([
|
||||
this.clearCountersButton.waitFor(),
|
||||
this.clearCountersButton.click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async enterCount(count: string, index: number = 0) {
|
||||
await Promise.all([
|
||||
this.counterInput.nth(index).waitFor(),
|
||||
this.counterInput.nth(index).fill(count),
|
||||
]);
|
||||
}
|
||||
|
||||
async removeCounter(index: number = 0) {
|
||||
await Promise.all([
|
||||
this.removeCountButton.nth(index).waitFor(),
|
||||
this.removeCountButton.nth(index).click(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
16
examples/counters/e2e/tests/increment_count.spec.ts
Normal file
16
examples/counters/e2e/tests/increment_count.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Increment Count", () => {
|
||||
test("should increase the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.incrementCount();
|
||||
await ui.incrementCount();
|
||||
await ui.incrementCount();
|
||||
|
||||
await expect(ui.total).toHaveText("3");
|
||||
});
|
||||
});
|
||||
17
examples/counters/e2e/tests/remove_counter.spec.ts
Normal file
17
examples/counters/e2e/tests/remove_counter.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("Remove Counter", () => {
|
||||
test("should decrement the number of counters", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.removeCounter(1);
|
||||
|
||||
await expect(ui.counters).toHaveText("2");
|
||||
});
|
||||
});
|
||||
19
examples/counters/e2e/tests/view_counters.spec.ts
Normal file
19
examples/counters/e2e/tests/view_counters.spec.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./fixtures/counters_page";
|
||||
|
||||
test.describe("View Counters", () => {
|
||||
test("should see the title", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await expect(page).toHaveTitle("Counters");
|
||||
});
|
||||
|
||||
test("should see the initial counts", async ({ page }) => {
|
||||
const counters = new CountersPage(page);
|
||||
await counters.goto();
|
||||
|
||||
await expect(counters.total).toHaveText("0");
|
||||
await expect(counters.counters).toHaveText("0");
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<link data-trunk rel="rust" data-wasm-opt="z" data-weak-refs/>
|
||||
<title>Counters</title>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use leptos::prelude::{signal::*, *};
|
||||
use leptos::prelude::*;
|
||||
|
||||
const MANY_COUNTERS: usize = 1000;
|
||||
|
||||
@@ -44,12 +44,13 @@ pub fn Counters() -> impl IntoView {
|
||||
<button on:click=clear_counters>"Clear Counters"</button>
|
||||
<p>
|
||||
"Total: "
|
||||
<span>
|
||||
<span data-testid="total">
|
||||
{move || {
|
||||
counters.get().iter().map(|(_, count)| count.get()).sum::<i32>().to_string()
|
||||
}}
|
||||
|
||||
</span> " from " <span>{move || counters.get().len().to_string()}</span>
|
||||
</span> " from "
|
||||
<span data-testid="counters">{move || counters.get().len().to_string()}</span>
|
||||
" counters."
|
||||
</p>
|
||||
<ul>
|
||||
|
||||
@@ -23,7 +23,7 @@ async fn inc() {
|
||||
div.inner_html(),
|
||||
"<button>Add Counter</button><button>Add 1000 \
|
||||
Counters</button><button>Clear Counters</button><p>Total: \
|
||||
<span>0</span> from <span>0</span> counters.</p><ul><!----></ul>"
|
||||
<span data-testid=\"total\">0</span> from <span data-testid=\"counters\">0</span> counters.</p><ul><!----></ul>"
|
||||
);
|
||||
|
||||
// add 3 counters
|
||||
@@ -38,7 +38,7 @@ async fn inc() {
|
||||
div.inner_html(),
|
||||
"<button>Add Counter</button><button>Add 1000 \
|
||||
Counters</button><button>Clear Counters</button><p>Total: \
|
||||
<span>0</span> from <span>3</span> \
|
||||
<span data-testid=\"total\">0</span> from <span data-testid=\"counters\">3</span> \
|
||||
counters.</p><ul><li><button>-1</button><input \
|
||||
type=\"text\"><span>0</span><button>+1</button><button>x</button></\
|
||||
li><li><button>-1</button><input \
|
||||
@@ -80,7 +80,7 @@ async fn inc() {
|
||||
div.inner_html(),
|
||||
"<button>Add Counter</button><button>Add 1000 \
|
||||
Counters</button><button>Clear Counters</button><p>Total: \
|
||||
<span>6</span> from <span>3</span> \
|
||||
<span data-testid=\"total\">6</span> from <span data-testid=\"counters\">3</span> \
|
||||
counters.</p><ul><li><button>-1</button><input \
|
||||
type=\"text\"><span>1</span><button>+1</button><button>x</button></\
|
||||
li><li><button>-1</button><input \
|
||||
@@ -105,7 +105,7 @@ async fn inc() {
|
||||
div.inner_html(),
|
||||
"<button>Add Counter</button><button>Add 1000 \
|
||||
Counters</button><button>Clear Counters</button><p>Total: \
|
||||
<span>5</span> from <span>2</span> \
|
||||
<span data-testid=\"total\">5</span> from <span data-testid=\"counters\">2</span> \
|
||||
counters.</p><ul><li><button>-1</button><input \
|
||||
type=\"text\"><span>2</span><button>+1</button><button>x</button></\
|
||||
li><li><button>-1</button><input \
|
||||
|
||||
@@ -5,12 +5,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
log = "0.4.22"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
web-sys = { version = "0.3", features = ["Clipboard", "Navigator"] }
|
||||
web-sys = { version = "0.3.70", features = ["Clipboard", "Navigator"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["NodeList"] }
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = { version = "0.3.70", features = ["NodeList"] }
|
||||
@@ -24,11 +24,7 @@ pub fn copy_to_clipboard(el: Element, content: &str) {
|
||||
evt.prevent_default();
|
||||
evt.stop_propagation();
|
||||
|
||||
let _ = window()
|
||||
.navigator()
|
||||
.clipboard()
|
||||
.expect("navigator.clipboard to be available")
|
||||
.write_text(&content);
|
||||
let _ = window().navigator().clipboard().write_text(&content);
|
||||
|
||||
el.set_inner_html(&format!("Copied \"{}\"", &content));
|
||||
});
|
||||
@@ -53,7 +49,6 @@ impl From<()> for Amount {
|
||||
}
|
||||
}
|
||||
|
||||
// .into() will automatically be called on the parameter
|
||||
pub fn add_dot(el: Element, amount: Amount) {
|
||||
use leptos::wasm_bindgen::JsCast;
|
||||
let el = el.unchecked_into::<web_sys::HtmlElement>();
|
||||
@@ -82,12 +77,17 @@ pub fn App() -> impl IntoView {
|
||||
let data = "Hello World!";
|
||||
|
||||
view! {
|
||||
<a href="#" use:copy_to_clipboard=data>"Copy \"" {data} "\" to clipboard"</a>
|
||||
<a href="#" use:copy_to_clipboard=data>
|
||||
"Copy \""
|
||||
{data}
|
||||
"\" to clipboard"
|
||||
</a>
|
||||
// automatically applies the directive to every root element in `SomeComponent`
|
||||
<SomeComponent use:highlight />
|
||||
<SomeComponent use:highlight/>
|
||||
// no value will default to `().into()`
|
||||
<button use:add_dot>"Add a dot"</button>
|
||||
// `5.into()` automatically called
|
||||
<button use:add_dot=5>"Add 5 dots"</button>
|
||||
// can manually call `.into()` to convert to the correct type
|
||||
// (automatically calling `.into()` prevents using generics in directive functions)
|
||||
<button use:add_dot=5.into()>"Add 5 dots"</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -7,19 +7,19 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1" }
|
||||
thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
|
||||
@@ -9,16 +9,16 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "tracing"] }
|
||||
reqwasm = "0.5"
|
||||
gloo-timers = { version = "0.3", features = ["futures"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
thiserror = "1"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber-wasm = "0.1"
|
||||
reqwasm = "0.5.0"
|
||||
gloo-timers = { version = "0.3.0", features = ["futures"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
log = "0.4.22"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
thiserror = "1.0"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
tracing-subscriber-wasm = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
|
||||
@@ -4,15 +4,15 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
throw_error = { path = "../../any_error/" }
|
||||
any_spawner = { path = "../../any_spawner/" }
|
||||
|
||||
# these are used to build the integration
|
||||
gtk = { version = "0.9.0", package = "gtk4" }
|
||||
next_tuple = { path = "../../next_tuple/" }
|
||||
gtk = { version = "0.8.0", package = "gtk4", optional = true }
|
||||
paste = "1.0.14"
|
||||
paste = "1.0"
|
||||
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
|
||||
[features]
|
||||
gtk = ["dep:gtk", "any_spawner/glib"]
|
||||
wasm = ["any_spawner/wasm-bindgen", "dep:console_error_panic_hook"]
|
||||
# we want to support using glib for the reactive runtime event loop
|
||||
any_spawner = { path = "../../any_spawner/", features = ["glib"] }
|
||||
# yes, we want effects to run: this is a "frontend," not a backend
|
||||
reactive_graph = { path = "../../reactive_graph", features = ["effects"] }
|
||||
|
||||
@@ -56,11 +56,12 @@ impl Mountable<LeptosGtk> for Element {
|
||||
.insert_before(&parent.0, marker.as_ref().map(|m| &m.0));
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable<LeptosGtk>,
|
||||
) -> bool {
|
||||
child.mount(parent, Some(self.as_ref()));
|
||||
true
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
if let Some(parent) = self.0.parent() {
|
||||
child.mount(&Element(parent), Some(self));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +80,8 @@ impl Mountable<LeptosGtk> for Text {
|
||||
.insert_before(&parent.0, marker.as_ref().map(|m| &m.0));
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable<LeptosGtk>,
|
||||
) -> bool {
|
||||
child.mount(parent, Some(self.as_ref()));
|
||||
true
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
self.0.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,16 +330,12 @@ where
|
||||
parent: &<LeptosGtk as Renderer>::Element,
|
||||
marker: Option<&<LeptosGtk as Renderer>::Node>,
|
||||
) {
|
||||
println!("mounting {}", std::any::type_name::<Widg>());
|
||||
self.children.mount(&self.widget, None);
|
||||
LeptosGtk::insert_node(parent, &self.widget, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable<LeptosGtk>,
|
||||
) -> bool {
|
||||
child.mount(parent, Some(self.widget.as_ref()));
|
||||
true
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
self.widget.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
#[cfg(feature = "gtk")]
|
||||
use gtk::{
|
||||
glib::Value, prelude::*, Application, ApplicationWindow, Orientation,
|
||||
Widget,
|
||||
};
|
||||
#[cfg(feature = "wasm")]
|
||||
use leptos::tachys::{dom::body, html::element, html::event as ev};
|
||||
use leptos::{
|
||||
logging,
|
||||
prelude::*,
|
||||
reactive_graph::{effect::Effect, owner::Owner, signal::RwSignal},
|
||||
Executor, For, ForProps,
|
||||
};
|
||||
#[cfg(feature = "gtk")]
|
||||
use leptos_gtk::{Element, LGtkWidget, LeptosGtk};
|
||||
use any_spawner::Executor;
|
||||
use gtk::{prelude::*, Application, ApplicationWindow, Orientation};
|
||||
use leptos::prelude::*;
|
||||
use leptos_gtk::LeptosGtk;
|
||||
use std::{mem, thread, time::Duration};
|
||||
#[cfg(feature = "gtk")]
|
||||
mod leptos_gtk;
|
||||
|
||||
const APP_ID: &str = "dev.leptos.Counter";
|
||||
@@ -22,59 +10,39 @@ const APP_ID: &str = "dev.leptos.Counter";
|
||||
// Basic GTK app setup from https://gtk-rs.org/gtk4-rs/stable/latest/book/hello_world.html
|
||||
fn main() {
|
||||
// use the glib event loop to power the reactive system
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
_ = Executor::init_glib();
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
_ = Executor::init_glib();
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
|
||||
app.connect_startup(|_| load_css());
|
||||
app.connect_startup(|_| load_css());
|
||||
|
||||
app.connect_activate(|app| {
|
||||
// Connect to "activate" signal of `app`
|
||||
let owner = Owner::new();
|
||||
let view = owner.with(ui);
|
||||
let (root, state) = leptos_gtk::root(view);
|
||||
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("TachyGTK")
|
||||
.child(&root)
|
||||
.build();
|
||||
// Present window
|
||||
window.present();
|
||||
mem::forget((owner, state));
|
||||
});
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
console_error_panic_hook::set_once();
|
||||
_ = Executor::init_wasm_bindgen();
|
||||
app.connect_activate(|app| {
|
||||
// Connect to "activate" signal of `app`
|
||||
let owner = Owner::new();
|
||||
let view = owner.with(ui);
|
||||
let mut state = view.build();
|
||||
state.mount(&body().into(), None);
|
||||
let (root, state) = leptos_gtk::root(view);
|
||||
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("TachyGTK")
|
||||
.child(&root)
|
||||
.build();
|
||||
// Present window
|
||||
window.present();
|
||||
mem::forget((owner, state));
|
||||
}
|
||||
});
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
#[cfg(feature = "gtk")]
|
||||
type Rndr = LeptosGtk;
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
type Rndr = Dom;
|
||||
|
||||
fn ui() -> impl Render<Rndr> {
|
||||
fn ui() -> impl Render<LeptosGtk> {
|
||||
let value = RwSignal::new(0);
|
||||
let rows = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
|
||||
Effect::new(move |_| {
|
||||
logging::log!("value = {}", value.get());
|
||||
println!("value = {}", value.get());
|
||||
});
|
||||
|
||||
// just an example of multithreaded reactivity
|
||||
#[cfg(feature = "gtk")]
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
value.update(|n| *n += 1);
|
||||
@@ -82,7 +50,10 @@ fn ui() -> impl Render<Rndr> {
|
||||
|
||||
vstack((
|
||||
hstack((
|
||||
button("-1", move || value.update(|n| *n -= 1)),
|
||||
button("-1", move || {
|
||||
println!("clicked -1");
|
||||
value.update(|n| *n -= 1);
|
||||
}),
|
||||
move || value.get().to_string(),
|
||||
button("+1", move || value.update(|n| *n += 1)),
|
||||
)),
|
||||
@@ -91,75 +62,36 @@ fn ui() -> impl Render<Rndr> {
|
||||
items.swap(1, 3);
|
||||
})
|
||||
}),
|
||||
hstack(For(ForProps::builder()
|
||||
.each(move || rows.get())
|
||||
.key(|k| *k)
|
||||
.children(|v| v)
|
||||
.build())),
|
||||
hstack(rows),
|
||||
))
|
||||
}
|
||||
|
||||
fn button(
|
||||
label: impl Render<Rndr>,
|
||||
label: impl Render<LeptosGtk>,
|
||||
callback: impl Fn() + Send + Sync + 'static,
|
||||
) -> impl Render<Rndr> {
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
leptos_gtk::button()
|
||||
.child(label)
|
||||
.connect("clicked", move |_| {
|
||||
callback();
|
||||
None
|
||||
})
|
||||
}
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
element::button()
|
||||
.on(ev::click, move |_| callback())
|
||||
.child(label)
|
||||
}
|
||||
) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::button()
|
||||
.child(label)
|
||||
.connect("clicked", move |_| {
|
||||
callback();
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn vstack(children: impl Render<Rndr>) -> impl Render<Rndr> {
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
element::div()
|
||||
.style(("display", "flex"))
|
||||
.style(("flex-direction", "column"))
|
||||
.style(("align-items", "center"))
|
||||
.style(("justify-content", "center"))
|
||||
.style(("margin", "1rem"))
|
||||
.child(children)
|
||||
}
|
||||
fn vstack(children: impl Render<LeptosGtk>) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
|
||||
fn hstack(children: impl Render<Rndr>) -> impl Render<Rndr> {
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
element::div()
|
||||
.style(("display", "flex"))
|
||||
.style(("align-items", "center"))
|
||||
.style(("justify-content", "center"))
|
||||
.style(("margin", "1rem"))
|
||||
.child(children)
|
||||
}
|
||||
fn hstack(children: impl Render<LeptosGtk>) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
|
||||
#[cfg(feature = "gtk")]
|
||||
fn load_css() {
|
||||
use gtk::{gdk::Display, CssProvider};
|
||||
|
||||
|
||||
@@ -13,31 +13,27 @@ panic = "abort"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
gloo-net = { version = "0.2", features = ["http"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = { version = "0.3.70", features = ["AbortController", "AbortSignal"] }
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[features]
|
||||
default = ["csr"]
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:leptos_actix",
|
||||
"leptos/ssr",
|
||||
]
|
||||
ssr = ["dep:actix-files", "dep:actix-web", "dep:leptos_actix", "leptos/ssr"]
|
||||
|
||||
[profile.wasm-release]
|
||||
inherits = "release"
|
||||
|
||||
@@ -11,22 +11,22 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
gloo-net = { version = "0.4", features = ["http"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0", optional = true }
|
||||
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2"
|
||||
tracing = "0.1.40"
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1", optional = true }
|
||||
web-sys = { version = "0.3.70", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -11,34 +11,35 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"experimental-islands",
|
||||
] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos", features = ["experimental-islands"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router"}
|
||||
leptos_router = { path = "../../router" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
gloo-net = { version = "0.4", features = ["http"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
axum = { version = "0.7", optional = true, features = ["http2"] }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = [
|
||||
tracing = "0.1.40"
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
axum = { version = "0.7.5", optional = true, features = ["http2"] }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = [
|
||||
"fs",
|
||||
"compression-gzip",
|
||||
"compression-br",
|
||||
], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0", optional = true }
|
||||
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2"
|
||||
lazy_static = "1.4.0"
|
||||
rust-embed = { version = "8", features = ["axum", "mime_guess", "tokio"], optional = true }
|
||||
mime_guess = { version = "2.0.4", optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1", optional = true }
|
||||
web-sys = { version = "0.3.70", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
lazy_static = "1.5"
|
||||
rust-embed = { version = "8.5", features = [
|
||||
"axum",
|
||||
"mime_guess",
|
||||
"tokio",
|
||||
], optional = true }
|
||||
mime_guess = { version = "2.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
|
||||
@@ -11,30 +11,30 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
log = "0.4"
|
||||
log = "0.4.22"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
leptos_server = { path = "../../leptos_server", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
gloo-net = { version = "0.5", features = ["http"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
axum = { version = "0.7", default-features = false, optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
http = { version = "1.0", optional = true }
|
||||
web-sys = { version = "0.3", features = [
|
||||
tracing = "0.1.40"
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
axum = { version = "0.7.5", default-features = false, optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
http = { version = "1.1", optional = true }
|
||||
web-sys = { version = "0.3.70", features = [
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Request",
|
||||
"Response",
|
||||
] }
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = { version = "0.4.37", features = [
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-futures = { version = "0.4.42", features = [
|
||||
"futures-core-03-stream",
|
||||
], optional = true }
|
||||
axum-js-fetch = { git = "https://github.com/seanaye/axum-js-fetch", optional = true }
|
||||
|
||||
@@ -7,32 +7,32 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
] }
|
||||
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
]
|
||||
|
||||
[profile.wasm-release]
|
||||
|
||||
@@ -7,33 +7,35 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
] }
|
||||
leptos_router = { path = "../../router" }
|
||||
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
|
||||
leptos_axum = { path = "../../integrations/axum", features = ["islands-router"], optional = true }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
leptos_axum = { path = "../../integrations/axum", features = [
|
||||
"islands-router",
|
||||
], optional = true }
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
]
|
||||
|
||||
[profile.wasm-release]
|
||||
|
||||
@@ -8,13 +8,13 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] } # for actual benchmarking, add `nightly` and `event-delegation` features
|
||||
leptos = { path = "../../leptos", features = ["csr"] } # for actual benchmarking, add `nightly` and `delegation` features
|
||||
# used in rand, but we need to enable js feature
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3"
|
||||
|
||||
@@ -9,7 +9,7 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
web-sys = "0.3"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -5,12 +5,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
log = "0.4.22"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = "0.3.70"
|
||||
@@ -10,16 +10,16 @@ codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
console_log = "1"
|
||||
console_log = "1.0"
|
||||
leptos = { path = "../../leptos", features = ["csr", "tracing"] }
|
||||
leptos_router = { path = "../../router" } #, features = ["tracing"] }
|
||||
leptos_router = { path = "../../router" } #, features = ["tracing"] }
|
||||
leptos_router_macro = { path = "../../router_macro" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
futures = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
futures = "0.3.30"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
tracing-subscriber = "0.3.18"
|
||||
tracing-subscriber-wasm = "0.1.0"
|
||||
tracing = "0.1.40"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
|
||||
@@ -72,6 +72,7 @@ pub fn ContactRoutes() -> impl MatchNestedRoutes<Dom> + Clone {
|
||||
<Route path=path!("/:id") view=Contact/>
|
||||
</ParentRoute>
|
||||
}
|
||||
.into_inner()
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
@@ -7,44 +7,53 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
server_fn = { path = "../../server_fn", features = ["serde-lite", "rkyv", "multipart"] }
|
||||
log = "0.4"
|
||||
simple_logger = "4.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs", "tracing", "trace"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
server_fn = { path = "../../server_fn", features = [
|
||||
"serde-lite",
|
||||
"rkyv",
|
||||
"multipart",
|
||||
"postcard",
|
||||
] }
|
||||
log = "0.4.22"
|
||||
simple_logger = "5.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = [
|
||||
"fs",
|
||||
"tracing",
|
||||
"trace",
|
||||
], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde_toml = "0.0.1"
|
||||
toml = "0.8.8"
|
||||
web-sys = { version = "0.3.67", features = ["FileList", "File"] }
|
||||
strum = { version = "0.25.0", features = ["strum_macros", "derive"] }
|
||||
notify = { version = "6.1.1", optional = true }
|
||||
pin-project-lite = "0.2.13"
|
||||
dashmap = { version = "5.5.3", optional = true }
|
||||
once_cell = { version = "1.19.0", optional = true }
|
||||
async-broadcast = { version = "0.6.0", optional = true }
|
||||
toml = "0.8.19"
|
||||
web-sys = { version = "0.3.70", features = ["FileList", "File"] }
|
||||
strum = { version = "0.26.3", features = ["strum_macros", "derive"] }
|
||||
notify = { version = "6.1", 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 }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:notify",
|
||||
"dep:dashmap",
|
||||
"dep:once_cell",
|
||||
"dep:async-broadcast",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:notify",
|
||||
"dep:dashmap",
|
||||
"dep:once_cell",
|
||||
"dep:async-broadcast",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -6,7 +6,8 @@ use server_fn::{
|
||||
client::{browser::BrowserClient, Client},
|
||||
codec::{
|
||||
Encoding, FromReq, FromRes, GetUrl, IntoReq, IntoRes, MultipartData,
|
||||
MultipartFormData, Rkyv, SerdeLite, StreamingText, TextStream,
|
||||
MultipartFormData, Postcard, Rkyv, SerdeLite, StreamingText,
|
||||
TextStream,
|
||||
},
|
||||
request::{browser::BrowserRequest, ClientReq, Req},
|
||||
response::{browser::BrowserResponse, ClientRes, Res},
|
||||
@@ -65,6 +66,7 @@ pub fn HomePage() -> impl IntoView {
|
||||
<h2>"Alternative Encodings"</h2>
|
||||
<ServerFnArgumentExample/>
|
||||
<RkyvExample/>
|
||||
<PostcardExample/>
|
||||
<FileUpload/>
|
||||
<FileUploadWithProgress/>
|
||||
<FileWatcher/>
|
||||
@@ -880,3 +882,67 @@ pub fn CustomClientExample() -> impl IntoView {
|
||||
})>Click me</button>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct PostcardData {
|
||||
name: String,
|
||||
age: u32,
|
||||
hobbies: Vec<String>,
|
||||
}
|
||||
|
||||
/// This server function uses Postcard for both input and output encoding.
|
||||
/// Postcard provides efficient binary serialization, almost as fast as rkyv, while also being
|
||||
/// serde compatible
|
||||
#[server(input = Postcard, output = Postcard)]
|
||||
pub async fn postcard_example(
|
||||
data: PostcardData,
|
||||
) -> Result<PostcardData, ServerFnError> {
|
||||
// Simulate some processing time
|
||||
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
|
||||
|
||||
// Modify the data to demonstrate server-side changes
|
||||
let mut modified_data = data.clone();
|
||||
modified_data.age += 1;
|
||||
modified_data.hobbies.push("Rust programming".to_string());
|
||||
|
||||
Ok(modified_data)
|
||||
}
|
||||
|
||||
/// This component demonstrates the usage of Postcard encoding with server functions.
|
||||
/// It allows incrementing the age of a person and shows how the data is
|
||||
/// serialized, sent to the server, processed, and returned.
|
||||
#[component]
|
||||
pub fn PostcardExample() -> impl IntoView {
|
||||
// Initialize the input data
|
||||
let (input, set_input) = signal(PostcardData {
|
||||
name: "Alice".to_string(),
|
||||
age: 30,
|
||||
hobbies: vec!["reading".to_string(), "hiking".to_string()],
|
||||
});
|
||||
|
||||
// Create a resource that will call the server function whenever the input changes
|
||||
let postcard_result = Resource::new(
|
||||
move || input.get(),
|
||||
|data| async move { postcard_example(data).await },
|
||||
);
|
||||
|
||||
view! {
|
||||
<h3>Using <code>postcard</code> encoding</h3>
|
||||
<p>"This example demonstrates using Postcard for efficient binary serialization."</p>
|
||||
<button on:click=move |_| {
|
||||
// Update the input data when the button is clicked
|
||||
set_input.update(|data| {
|
||||
data.age += 1;
|
||||
});
|
||||
}>
|
||||
"Increment Age"
|
||||
</button>
|
||||
// Display the current input data
|
||||
<p>"Input: " {move || format!("{:?}", input.get())}</p>
|
||||
<Transition>
|
||||
// Display the result from the server, which will update automatically
|
||||
// when the input changes due to the resource
|
||||
<p>"Result: " {move || postcard_result.get().map(|r| format!("{:?}", r))}</p>
|
||||
</Transition>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -7,20 +7,20 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos"}
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
lazy_static = "1.5"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = ["time"] }
|
||||
wasm-bindgen = "0.2"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.39", features = ["time"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
|
||||
@@ -7,37 +7,39 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos", features = ["hydration" ] } #"nightly", "hydration"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
lazy_static = "1.5"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"hydration",
|
||||
] } #"nightly", "hydration"] }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
"time",
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
"time",
|
||||
], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"dep:leptos_axum",
|
||||
"leptos_router/ssr",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"dep:leptos_axum",
|
||||
"leptos_router/ssr",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<AutoReload options=options.clone() />
|
||||
<AutoReload options=options.clone()/>
|
||||
<HydrationScripts options/>
|
||||
<MetaTags/>
|
||||
</head>
|
||||
@@ -64,15 +64,24 @@ pub fn App() -> impl IntoView {
|
||||
<a href="/admin">"Admin"</a>
|
||||
<Transition>
|
||||
<ActionForm action=toggle_admin>
|
||||
<input type="hidden" name="is_admin"
|
||||
value=move || (!is_admin.get().and_then(|n| n.ok()).unwrap_or_default()).to_string()
|
||||
<input
|
||||
type="hidden"
|
||||
name="is_admin"
|
||||
value=move || {
|
||||
(!is_admin.get().and_then(|n| n.ok()).unwrap_or_default())
|
||||
.to_string()
|
||||
}
|
||||
/>
|
||||
|
||||
<button>
|
||||
{move || if is_admin.get().and_then(Result::ok).unwrap_or_default() {
|
||||
"Log Out"
|
||||
} else {
|
||||
"Log In"
|
||||
{move || {
|
||||
if is_admin.get().and_then(Result::ok).unwrap_or_default() {
|
||||
"Log Out"
|
||||
} else {
|
||||
"Log In"
|
||||
}
|
||||
}}
|
||||
|
||||
</button>
|
||||
</ActionForm>
|
||||
</Transition>
|
||||
@@ -139,9 +148,15 @@ fn HomePage() -> impl IntoView {
|
||||
<li>
|
||||
<a href=format!("/post/{}", post.id)>{post.title.clone()}</a>
|
||||
"|"
|
||||
<a href=format!("/post_in_order/{}", post.id)>{post.title.clone()} "(in order)"</a>
|
||||
<a href=format!(
|
||||
"/post_in_order/{}",
|
||||
post.id,
|
||||
)>{post.title.clone()} "(in order)"</a>
|
||||
"|"
|
||||
<a href=format!("/post_partially_blocked/{}", post.id)>{post.title} "(partially blocked)"</a>
|
||||
<a href=format!(
|
||||
"/post_partially_blocked/{}",
|
||||
post.id,
|
||||
)>{post.title} "(partially blocked)"</a>
|
||||
</li>
|
||||
</For>
|
||||
</ul>
|
||||
@@ -204,12 +219,11 @@ fn Post() -> impl IntoView {
|
||||
Ok(comments) => Ok(view! {
|
||||
<h1>"Comments"</h1>
|
||||
<ul>
|
||||
{comments.into_iter()
|
||||
.map(|comment| view! {
|
||||
<li>{comment}</li>
|
||||
})
|
||||
.collect_view()
|
||||
}
|
||||
{comments
|
||||
.into_iter()
|
||||
.map(|comment| view! { <li>{comment}</li> })
|
||||
.collect_view()}
|
||||
|
||||
</ul>
|
||||
}),
|
||||
_ => Err(PostError::ServerError),
|
||||
@@ -237,17 +251,13 @@ fn Post() -> impl IntoView {
|
||||
}
|
||||
}>{post_view}</ErrorBoundary>
|
||||
</Suspense>
|
||||
<Suspense fallback=move || view! { <p>"Loading comments..."</p> }>
|
||||
{comments_view}
|
||||
</Suspense>
|
||||
<Suspense fallback=move || view! { <p>"Loading comments..."</p> }>{comments_view}</Suspense>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Admin() -> impl IntoView {
|
||||
view! {
|
||||
<p>"You can only see this page if you're logged in."</p>
|
||||
}
|
||||
view! { <p>"You can only see this page if you're logged in."</p> }
|
||||
}
|
||||
|
||||
// Dummy API
|
||||
|
||||
13
examples/static_routing/.gitignore
vendored
Normal file
13
examples/static_routing/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
pkg
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# node e2e test tools and outputs
|
||||
node_modules/
|
||||
test-results/
|
||||
end2end/playwright-report/
|
||||
playwright/.cache/
|
||||
115
examples/static_routing/Cargo.toml
Normal file
115
examples/static_routing/Cargo.toml
Normal file
@@ -0,0 +1,115 @@
|
||||
[package]
|
||||
name = "static_routing"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"hydration",
|
||||
] } #"nightly", "hydration"] }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = [
|
||||
"fs",
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
], optional = true }
|
||||
tokio-stream = { version = "0.1", features = ["fs"], optional = true }
|
||||
futures = "0.3"
|
||||
wasm-bindgen = "0.2.93"
|
||||
notify = { version = "6", optional = true }
|
||||
http = { version = "1", optional = true }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"dep:tokio-stream",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"dep:leptos_axum",
|
||||
"leptos_router/ssr",
|
||||
"dep:notify",
|
||||
"dep:http"
|
||||
]
|
||||
|
||||
[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 = "ssr_modes"
|
||||
# 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"
|
||||
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
|
||||
style-file = "style/main.scss"
|
||||
# Assets source dir. All files found here will be copied and synchronized to site-root.
|
||||
# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.
|
||||
#
|
||||
# Optional. Env: LEPTOS_ASSETS_DIR.
|
||||
assets-dir = "assets"
|
||||
# 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:3007"
|
||||
# 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 = "npx playwright test"
|
||||
end2end-dir = "end2end"
|
||||
# The browserlist query used for optimizing the CSS.
|
||||
browserquery = "defaults"
|
||||
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
|
||||
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
|
||||
|
||||
lib-profile-release = "wasm-release"
|
||||
21
examples/static_routing/LICENSE
Normal file
21
examples/static_routing/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 henrik
|
||||
|
||||
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.
|
||||
8
examples/static_routing/Makefile.toml
Normal file
8
examples/static_routing/Makefile.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
extend = [
|
||||
{ path = "../cargo-make/main.toml" },
|
||||
{ path = "../cargo-make/cargo-leptos.toml" },
|
||||
]
|
||||
|
||||
[env]
|
||||
|
||||
CLIENT_PROCESS_NAME = "ssr_modes_axum"
|
||||
11
examples/static_routing/README.md
Normal file
11
examples/static_routing/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Static Routing Example
|
||||
|
||||
This example shows the static routing features, which can be used to generate the HTML content for some routes before a request.
|
||||
|
||||
## Getting Started
|
||||
|
||||
See the [Examples README](../README.md) for setup and run instructions.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run `cargo leptos watch` to run this example.
|
||||
BIN
examples/static_routing/assets/favicon.ico
Normal file
BIN
examples/static_routing/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
3
examples/static_routing/posts/post1.md
Normal file
3
examples/static_routing/posts/post1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# My first blog post
|
||||
|
||||
Having a blog is *fun*.
|
||||
3
examples/static_routing/posts/post2.md
Normal file
3
examples/static_routing/posts/post2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# My second blog post
|
||||
|
||||
Coming up with content is hard.
|
||||
3
examples/static_routing/posts/post3.md
Normal file
3
examples/static_routing/posts/post3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# My third blog post
|
||||
|
||||
Could I just have AI write this for me instead?
|
||||
3
examples/static_routing/posts/post4.md
Normal file
3
examples/static_routing/posts/post4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# My fourth post
|
||||
|
||||
Here is some content. It should regenerate the static page.
|
||||
2
examples/static_routing/rust-toolchain.toml
Normal file
2
examples/static_routing/rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "stable" # test change
|
||||
323
examples/static_routing/src/app.rs
Normal file
323
examples/static_routing/src/app.rs
Normal file
@@ -0,0 +1,323 @@
|
||||
use std::path::Path;
|
||||
|
||||
use futures::{channel::mpsc, Stream};
|
||||
use leptos::prelude::*;
|
||||
use leptos_meta::MetaTags;
|
||||
use leptos_meta::*;
|
||||
use leptos_router::{
|
||||
components::{FlatRoutes, Redirect, Route, Router},
|
||||
hooks::use_params,
|
||||
params::Params,
|
||||
path,
|
||||
static_routes::StaticRoute,
|
||||
SsrMode,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
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 {
|
||||
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||
provide_meta_context();
|
||||
let fallback = || view! { "Page not found." }.into_view();
|
||||
|
||||
view! {
|
||||
<Stylesheet id="leptos" href="/pkg/ssr_modes.css"/>
|
||||
<Title text="Welcome to Leptos"/>
|
||||
<Meta name="color-scheme" content="dark light"/>
|
||||
<Router>
|
||||
<nav>
|
||||
<a href="/">"Home"</a>
|
||||
</nav>
|
||||
<main>
|
||||
<FlatRoutes fallback>
|
||||
<Route
|
||||
path=path!("/")
|
||||
view=HomePage
|
||||
ssr=SsrMode::Static(
|
||||
StaticRoute::new().regenerate(|_| watch_path(Path::new("./posts"))),
|
||||
)
|
||||
/>
|
||||
|
||||
<Route
|
||||
path=path!("/about")
|
||||
view=move || view! { <Redirect path="/"/> }
|
||||
ssr=SsrMode::Static(StaticRoute::new())
|
||||
/>
|
||||
|
||||
<Route
|
||||
path=path!("/post/:slug/")
|
||||
view=Post
|
||||
ssr=SsrMode::Static(
|
||||
StaticRoute::new()
|
||||
.prerender_params(|| async move {
|
||||
[("slug".into(), list_slugs().await.unwrap_or_default())]
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
.regenerate(|params| {
|
||||
let slug = params.get("slug").unwrap();
|
||||
watch_path(Path::new(&format!("./posts/{slug}.md")))
|
||||
}),
|
||||
)
|
||||
/>
|
||||
|
||||
</FlatRoutes>
|
||||
</main>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn HomePage() -> impl IntoView {
|
||||
// load the posts
|
||||
let posts = Resource::new(|| (), |_| list_posts());
|
||||
let posts = move || {
|
||||
posts
|
||||
.get()
|
||||
.map(|n| n.unwrap_or_default())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
view! {
|
||||
<h1>"My Great Blog"</h1>
|
||||
<Suspense fallback=move || view! { <p>"Loading posts..."</p> }>
|
||||
<ul>
|
||||
<For each=posts key=|post| post.slug.clone() let:post>
|
||||
<li>
|
||||
<a href=format!("/post/{}/", post.slug)>{post.title.clone()}</a>
|
||||
</li>
|
||||
</For>
|
||||
</ul>
|
||||
</Suspense>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Params, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PostParams {
|
||||
slug: Option<String>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Post() -> impl IntoView {
|
||||
let query = use_params::<PostParams>();
|
||||
let slug = move || {
|
||||
query
|
||||
.get()
|
||||
.map(|q| q.slug.unwrap_or_default())
|
||||
.map_err(|_| PostError::InvalidId)
|
||||
};
|
||||
let post_resource = Resource::new_blocking(slug, |slug| async move {
|
||||
match slug {
|
||||
Err(e) => Err(e),
|
||||
Ok(slug) => get_post(slug)
|
||||
.await
|
||||
.map(|data| data.ok_or(PostError::PostNotFound))
|
||||
.map_err(|e| PostError::ServerError(e.to_string())),
|
||||
}
|
||||
});
|
||||
|
||||
let post_view = move || {
|
||||
Suspend::new(async move {
|
||||
match post_resource.await {
|
||||
Ok(Ok(post)) => {
|
||||
Ok(view! {
|
||||
<h1>{post.title.clone()}</h1>
|
||||
<p>{post.content.clone()}</p>
|
||||
|
||||
// since we're using async rendering for this page,
|
||||
// this metadata should be included in the actual HTML <head>
|
||||
// when it's first served
|
||||
<Title text=post.title/>
|
||||
<Meta name="description" content=post.content/>
|
||||
})
|
||||
}
|
||||
Ok(Err(e)) | Err(e) => {
|
||||
Err(PostError::ServerError(e.to_string()))
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
view! {
|
||||
<em>"The world's best content."</em>
|
||||
<Suspense fallback=move || view! { <p>"Loading post..."</p> }>
|
||||
<ErrorBoundary fallback=|errors| {
|
||||
#[cfg(feature = "ssr")]
|
||||
expect_context::<leptos_axum::ResponseOptions>()
|
||||
.set_status(http::StatusCode::NOT_FOUND);
|
||||
view! {
|
||||
<div class="error">
|
||||
<h1>"Something went wrong."</h1>
|
||||
<ul>
|
||||
{move || {
|
||||
errors
|
||||
.get()
|
||||
.into_iter()
|
||||
.map(|(_, error)| view! { <li>{error.to_string()}</li> })
|
||||
.collect::<Vec<_>>()
|
||||
}}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}>{post_view}</ErrorBoundary>
|
||||
</Suspense>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PostError {
|
||||
#[error("Invalid post ID.")]
|
||||
InvalidId,
|
||||
#[error("Post not found.")]
|
||||
PostNotFound,
|
||||
#[error("Server error: {0}.")]
|
||||
ServerError(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Post {
|
||||
slug: String,
|
||||
title: String,
|
||||
content: String,
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn list_slugs() -> Result<Vec<String>, ServerFnError> {
|
||||
use tokio::fs;
|
||||
use tokio_stream::wrappers::ReadDirStream;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
let files = ReadDirStream::new(fs::read_dir("./posts").await?);
|
||||
Ok(files
|
||||
.filter_map(|entry| {
|
||||
let entry = entry.ok()?;
|
||||
let path = entry.path();
|
||||
if !path.is_file() {
|
||||
return None;
|
||||
}
|
||||
let extension = path.extension()?;
|
||||
if extension != "md" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let slug = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or_default()
|
||||
.replace(".md", "");
|
||||
Some(slug)
|
||||
})
|
||||
.collect()
|
||||
.await)
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn list_posts() -> Result<Vec<Post>, ServerFnError> {
|
||||
println!("calling list_posts");
|
||||
|
||||
use futures::TryStreamExt;
|
||||
use tokio::fs;
|
||||
use tokio_stream::wrappers::ReadDirStream;
|
||||
|
||||
let files = ReadDirStream::new(fs::read_dir("./posts").await?);
|
||||
files
|
||||
.try_filter_map(|entry| async move {
|
||||
let path = entry.path();
|
||||
if !path.is_file() {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(extension) = path.extension() else {
|
||||
return Ok(None);
|
||||
};
|
||||
if extension != "md" {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let slug = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or_default()
|
||||
.replace(".md", "");
|
||||
let content = fs::read_to_string(path).await?;
|
||||
// world's worst Markdown frontmatter parser
|
||||
let title = content.lines().next().unwrap().replace("# ", "");
|
||||
|
||||
Ok(Some(Post {
|
||||
slug,
|
||||
title,
|
||||
content,
|
||||
}))
|
||||
})
|
||||
.try_collect()
|
||||
.await
|
||||
.map_err(ServerFnError::from)
|
||||
}
|
||||
|
||||
#[server]
|
||||
pub async fn get_post(slug: String) -> Result<Option<Post>, ServerFnError> {
|
||||
println!("reading ./posts/{slug}.md");
|
||||
let content =
|
||||
tokio::fs::read_to_string(&format!("./posts/{slug}.md")).await?;
|
||||
// world's worst Markdown frontmatter parser
|
||||
let title = content.lines().next().unwrap().replace("# ", "");
|
||||
|
||||
Ok(Some(Post {
|
||||
slug,
|
||||
title,
|
||||
content,
|
||||
}))
|
||||
}
|
||||
|
||||
#[allow(unused)] // path is not used in non-SSR
|
||||
fn watch_path(path: &Path) -> impl Stream<Item = ()> {
|
||||
#[allow(unused)]
|
||||
let (mut tx, rx) = mpsc::channel(0);
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
use notify::RecursiveMode;
|
||||
use notify::Watcher;
|
||||
|
||||
let mut watcher =
|
||||
notify::recommended_watcher(move |res: Result<_, _>| {
|
||||
if res.is_ok() {
|
||||
// if this fails, it's because the buffer is full
|
||||
// this means we've already notified before it's regenerated,
|
||||
// so this page will be queued for regeneration already
|
||||
_ = tx.try_send(());
|
||||
}
|
||||
})
|
||||
.expect("could not create watcher");
|
||||
|
||||
// Add a path to be watched. All files and directories at that path and
|
||||
// below will be monitored for changes.
|
||||
watcher
|
||||
.watch(path, RecursiveMode::NonRecursive)
|
||||
.expect("could not watch path");
|
||||
|
||||
// we want this to run as long as the server is alive
|
||||
std::mem::forget(watcher);
|
||||
}
|
||||
|
||||
rx
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user