Compare commits

...

97 Commits

Author SHA1 Message Date
Greg Johnston
c750f57ddc v0.6.10 2024-04-02 09:39:53 -04:00
Joseph Cruz
cc1f6f0a94 chore(ci): run semver checks on push (#2483) 2024-04-01 20:38:28 -04:00
Greg Johnston
a9034a92b0 fix: handle directives properly in SSR mode (closes #2488) (#2477) 2024-04-01 17:29:30 -04:00
zakstucke
9f1c09e131 feat: add View::on support for CoreComponent::{DynChild, Each} (#2422) 2024-04-01 17:09:05 -04:00
Greg Johnston
b79037b96f fix: correctly handle empty view! {} in hot-reloading code (closes #2421) (#2478) 2024-04-01 16:23:29 -04:00
Greg Johnston
41f3c46830 chore: bump nightly version in examples (#2479) 2024-04-01 15:16:53 -04:00
Greg Johnston
bfac4cba2a chore: cargo fmt 2024-03-31 14:12:33 -04:00
Paolo Barbolini
3e18edb8f9 chore: add repository field to server_fn_macro (#2474) 2024-03-31 14:10:47 -04:00
Joseph Cruz
e926ff24a6 ci: disable semver checks (#2471) 2024-03-30 20:05:20 +00:00
Gunnar Raßmann
d528cbd828 Fix: Environment variables do not overwrite Config.toml options (#2433)
* Fix environment variable parsing

* Fix failing tests

dfgdfgfd

dsf

* Add new test
2024-03-30 00:02:52 +00:00
Alex Lazar
642504f2ba Remove panic for axum ResponseOptions (#2468) 2024-03-29 07:37:12 +00:00
zakstucke
fd2817de26 Allow CDN_PKG_PATH at runtime as well as current build time, preferring it when available. (#2466) 2024-03-28 08:30:54 +00:00
Bart Toersche
73b8c7872e Fix: Small fix for location hash/fragment (#2464) 2024-03-27 06:45:29 +00:00
martin frances
f3d19ca744 Minor: Ran cargo clippy --fix (#2461)
Manually reviewed the changes. All look like reasonable nudges.

A summary :-

In one place removed a redundant call to .clone().

In two places, now using clone_from() which clippy says
**MAY** be an optimisation.
2024-03-23 18:27:31 -07:00
boyswan
0abcc348ca Persist parent span context within resource fetchers (#2456) 2024-03-22 15:51:50 -07:00
Joseph Cruz
572ae5bbdf test(ci): check semver (#2450)
* test(ci): check semver

* chore: simulate change

* fix(ci): add checkout

* fix(ci): version typo

* chore: remove simulated change
2024-03-22 15:51:13 -07:00
martin frances
0b70949118 Bumped base64 to 0.22. (#2457) 2024-03-22 15:07:04 -07:00
martin frances
5819014ccc Chore(ci) bumping tj-actions/changed-files to version 43. (#2454) 2024-03-22 07:23:57 -04:00
Joseph Cruz
630fd4570d fix(ci): trunk command not found (#2453)
* chore: simulate change

* chore: remove print trunk version

* Revert "chore: remove print trunk version"

This reverts commit c203a83b44.

* chore(ci): use jetli/trunk-action

* chore: remove simulated change
2024-03-22 07:23:48 -04:00
Ratul
d1560f9e1f Added missing link for #[server] macro (#2437)
* Added missing link for #[server] macro

Added missing link for #[server] macro

* Removed spurious entry
2024-03-20 14:24:54 -07:00
martin frances
841d7a690a chore: examples/tailwind_axum bumped tailwindcss to 3.4.2. (#2443) 2024-03-19 09:40:24 -07:00
sify21
104c09f3bf register server_fn first to allow for wildcard Route path (#2435)
It's normal to have a `NotFound` page with a wildcard path like this
```
<Routes>
    ...
    <Route path="*any" view=NotFound>
</Routes>
```
In `ssr` mode, most servers do a `first match win` approach, so we
should register server functions before view routes, or else a wildcard
route would block all api requests.

https://discord.com/channels/1031524867910148188/1218508054442545185

Signed-off-by: 司芳源 <sify21@163.com>
2024-03-19 09:37:42 -07:00
Joseph Cruz
ac75999c9f chore(ci): upgrade actions to node 20 (#2444)
* chore(ci): install jq with apt

* chore(ci): install trunk with cargo

* chore(ci): replace toolchain action

* chore(ci): upgrade pnpm cache action

* chore: simulate change

* fix(ci): pnpm cache action typo

* chore: remove simulated change
2024-03-19 09:36:30 -07:00
Richard Laughlin
7ef186f642 For the session_auth_axum example, move the passhash into a separate (#2446)
non-serializable struct.

This prevents it from being returned in the
get_user() API, and prevents it from being unintentionally returned on any
new API the end-user may create on top of this example code.
2024-03-19 09:35:53 -07:00
Joseph Cruz
fda4dba237 build(examples): clean more output (#2420)
* chore(examples): update workspace members

* build(examples): clean e2e crates

* build(examples): clean pkg directories

* chore: remove simulated change comment

* chore: add simulated change

* chore: remove simulated change
2024-03-18 11:58:37 -04:00
Roland Fredenhagen
4e578e335b chore: update attribute-derive (#2438) 2024-03-18 11:39:34 -04:00
Joseph Cruz
97fd8ff6c4 fix(ci): leptos examples fail with bindgen schema error (#2428) 2024-03-13 22:33:54 -04:00
battmdpkq
4faf3fa894 chore: fix types in some comments (#2413)
Signed-off-by: battmdpkq <cmaker@163.com>
2024-03-09 07:38:25 -05:00
Greg Johnston
480d741749 chore: update to gloo-net 0.5 (closes #2411) (#2416) 2024-03-08 15:22:12 -05:00
Álvaro Mondéjar
7928f61401 chore: add lint to disallow prints to stdout (#2404) 2024-03-08 13:18:37 -05:00
Giovanni
2b4f5e0f58 docs: runtime error if setting the same event listener 2x rather than silent failure (#2383)
Delegated event listeners do not support adding more than one event listener of the same type. This can cause confusion if two listeners are added, as one is silently dropped.
2024-03-07 16:49:23 -05:00
Giovanni
943a992570 fix: re-throw errors in delegated event listeners (#2382) 2024-03-07 16:48:21 -05:00
ARSON
372a241d78 feat: allow #[prop(attrs)] on slots (#2396) 2024-03-04 17:34:21 -08:00
Chris Biscardi
c06f6bede2 fix: remove erroneous debug println!()s in islands (#2402) 2024-03-04 06:56:18 -05:00
benwis
3e93a686f4 Fix and release deps 2024-03-03 17:04:34 -08:00
benwis
34cdff4cb3 Update deps in one crate to 0.6.8 2024-03-03 17:02:50 -08:00
John Lewis
530087d77d Add MessagePack codec (#2371)
* feat: added messagepack codec

* fix: deserialize msgpack from bytes, not string
2024-03-03 13:54:23 -08:00
martin frances
4bb43f6207 examples/todomvc - Rename Todos::new() Todos::default(). (#2390) 2024-03-03 13:48:40 -08:00
benwis
9e2fb62857 0.6.8 2024-03-02 18:01:10 -08:00
Ben Wishovich
1da2fff706 Fix missed stuff (#2398) 2024-03-02 17:57:20 -08:00
Greg Johnston
9fd2987447 fix: correctly reset hydration status in islands mode Suspense (closes #2332) (#2393) 2024-03-02 11:57:35 -05:00
zroug
7996f835d0 fix: remove unnecessary trait bound PartialEq from create_owning_memo (#2394) 2024-03-02 07:27:22 -05:00
Greg Johnston
d72b12524e Merge pull request #2395 from leptos-rs/int-ax-doc 2024-03-01 20:08:18 -05:00
Greg Johnston
8e79c5be5c fix: ignore as with other doctests for now 2024-03-01 18:39:55 -05:00
Greg Johnston
de25658c36 Merge pull request #2392 from paul-hansen/fix-ci
fix(ci): "needless borrow" error and example never exiting
2024-03-01 18:37:48 -05:00
Paul Hansen
e2e35a9659 fix(ci) Wait a bit longer for server to start
It took longer than I thought in Github and barely worked, giving it a
bit more of a buffer.
2024-03-01 15:47:59 -06:00
Paul Hansen
bf1ba589c5 fix(ci): Another attempt to fix hanging example 2024-03-01 15:41:22 -06:00
Sam Judelson
f70ebc1289 docs: add note on how to get ResponseOptions (#2380) 2024-03-01 10:47:02 -05:00
Paul Hansen
3cab09e015 fix(ci): error_boundary example never exiting 2024-03-01 09:21:58 -06:00
Paul Hansen
b431315f7c fix(ci): "needless borrow" error 2024-03-01 09:21:58 -06:00
Baptiste
5b40881e77 fix: specify path to wasm_bindgen in island macro (#2387) 2024-03-01 10:15:19 -05:00
benwis
59d3cce3be 0.6.7 2024-02-29 13:38:09 -08:00
Paul Hansen
6a83161368 chore: add MSRV (#2360) 2024-02-28 07:19:09 -05:00
Marc-Stefan Cassola
4b00c16cb9 added hashes generated from cargo-leptos (#2373)
* added hashes generated from cargo-leptos

* Added config option to disable frontend file name hash
2024-02-27 16:28:27 -08:00
haslersn
6d6019b956 fix: do not strip query in redirect hook when using client-side navigation (#2376) 2024-02-27 09:06:48 -08:00
Sam Judelson
3540291065 docs: Resource::read() in doc examples with Resource::get() (#2372) 2024-02-26 21:37:29 -05:00
zoomiti
4809cf473e feat: provide leptos_router::Method via context (#1808) (#2315) 2024-02-26 21:25:53 -05:00
Tadas Dailyda
aa977001c1 feat: add support for trailing slashes (closes #2154) (#2217) 2024-02-26 20:56:44 -05:00
Greg Johnston
c16189f095 Merge pull request #2362 from leptos-rs/remove-deprecation
chore(ci): fix failing CI by removing deprecation note
2024-02-24 13:05:02 -05:00
Greg Johnston
d0a013c248 chore(ci): omit a few feature flag in CI 2024-02-24 11:55:36 -05:00
Greg Johnston
531ea74e33 chore: cargo fmt in leptos_macro 2024-02-24 07:12:46 -05:00
Greg Johnston
545e87e540 chore(ci): fix failing CI by removing deprecation note 2024-02-24 07:07:31 -05:00
Joseph Cruz
2ca30a0b2d ci(examples): build hackernews_js_fetch with deno (#2344)
* ci(examples): refactor process management

* ci(examples): build hackernews_js_fetch with deno

* ci(workflows): detect hackernews_js_fetch change

* chore(web-report): report deno usage

* chore(web-report): ignore gtk example

* ci(todo_app_sqlite): simulate change

* ci(workflows): install deno

* ci(todo_app_sqlite): remove simulated change
2024-02-23 13:40:44 -05:00
zoomiti
753bf1ed54 Fix Broken Doc links and Deprecate FromUtf8Error in oco.rs (#2318)
* fix: deprecate `FromUtf8Error` in `oco.rs`

* chore: fix broken doc links (#859)

* chore: fix broken doc link to server attribute macro

* cargo fmt
2024-02-21 19:24:40 -08:00
Sam Judelson
37c6387fea finish doc sentence (#2348) 2024-02-21 19:21:57 -08:00
Janu (Janeshwar) Cambrelen
0a73487152 feat(leptos-axum): propagate trace context to server functions (#2340) 2024-02-21 19:21:00 -08:00
Sam Judelson
747aba0d7f add comment specifying edgecase of server function prefixes (#2345) 2024-02-20 18:17:20 -08:00
Sam Judelson
04cf47d5da Update suspense_component.rs documentation to use .get() instead of .read() (#2346) 2024-02-20 18:16:03 -08:00
rjmac
47abe00993 fix: don't leak canceled timeouts (#2331)
Instead of using `Closure::once_into_js`, this uses `into_js_value`,
which uses weak references to clean up the closure when Javascript no
longer has need of it.

It would be nice to make this (and the similar interval function) drop
the callback promptly when cancelled, but I don't think that's
possible while keeping the handles Copy.

Fixes #2330

Co-authored-by: Robert Macomber <robertm@mox>
2024-02-19 21:17:26 -05:00
eliza
aa3700ffb9 feat: add impl_from argument to #[server] proc_macro (#2335)
* Add `impl_into` argument

* Add `impl_into` argument

* Revert unneeded changes

* Address review comments

Rename `impl_into` back to be `impl_from`
Rework docstring

* Fix typo in docstring
2024-02-19 21:16:46 -05:00
Aphek
0770b87cb7 feat: Add owning memos to allow memos that re-use the previous value (#2139) 2024-02-19 21:16:19 -05:00
benwis
330ebdb018 v0.6.6 2024-02-19 13:48:32 -08:00
benwis
c3179d88cf Moved leptos-spin-macro dep to released version 2024-02-19 12:54:43 -08:00
Sahaj
ffcf3c2952 example: fix href path in tailwind_csr example (#2328) 2024-02-17 13:10:07 -05:00
haslersn
001ca5148e fix: handle cross-origin redirects in server function redirect hook (#2329)
In client-side navigation we now handle redirects returned from
server functions by resolving the location against the current
origin as a base. The base is only relevant if the location
doesn't already include an origin. This fixes cross-origin
redirects.

Note: in order to handle redirects in the same way as the browser
would handle them, we need to use the server function's URL
(typically `<origin>/api/something`) as a base. I leave this as
a TODO for a future leptos version, because it probably
requires changing the signature of the `server_fn` redirect hook.

In order to not be affected by a future breaking change, users
should already start making sure that their redirect locations
either include an origin or at least start with a single slash
(e.g. `Location: /foo`).
2024-02-17 13:09:39 -05:00
Greg Johnston
1e000afa78 examples: fix CSS file name in tailwind_axum (#2324) 2024-02-17 12:56:03 -05:00
Greg Johnston
0f7b8841b2 chore(ci): reduce set of tested features to prevent running out of disk space in server_fn (#2320) 2024-02-16 20:26:26 -05:00
Greg Johnston
7dc0441f6c docs: log error on failing to convert form to ServerFn type, in addition to setting action value (#2319) 2024-02-16 17:11:14 -05:00
Joseph Cruz
0a321a1bd7 docs(examples): update docs (#2313)
* docs(examples): fix metadata typo

* docs(examples): update first step about using cargo make
2024-02-16 13:32:01 -05:00
Greg Johnston
88742952f0 fix: Transition in hydrate mode that isn't initially created (closes #2279) (#2314) 2024-02-16 08:16:09 -05:00
martin frances
8a4b972e0b chore: bump config to 0.14 (#2302) 2024-02-15 20:24:12 -05:00
zoomiti
95bd9cc544 feat: use CDN_PKG_PATH at build time to set alternate base URL for JS/WASM bundles (#2281) (#2283) 2024-02-15 20:21:47 -05:00
Marc-Stefan Cassola
23bc892a24 fix: #[server] macro error type detection (#2298)
In most cases when you return `Result<..., ServerFnError<E>>` this worked but when you tried
`Result<..., leptos::ServerFnError<E>>` then it didn't.
2024-02-15 20:20:41 -05:00
Esteban Borai
830fba794e docs: add missing provide_meta_context() in example (#2311)
Otherwise user gets:

```
use_head() is being called without a MetaContext being provided. We'll automatically create and provide one, but if this is being called in a child route it may cause bugs. To be safe, you should provide_meta_context() somewhere in the root of the app.
```
2024-02-15 20:19:07 -05:00
martin frances
98633c8700 chore(ci): update node version for GitHub Actions (#2303) 2024-02-15 20:17:12 -05:00
David Rebbe
b0f5c39711 example: replace yanked version of session_auth_axum crate (#2310) 2024-02-15 20:16:26 -05:00
Greg Johnston
b54aa7f3f5 Merge pull request #2294 from agilarity/add-cargo-make-leptos 2024-02-15 18:52:37 -05:00
Sam Judelson
e33ee7ec99 pub export server is either from leptos_macro or leptos_spin_macro depending on if spin feature is enabled. (#2280)
* leptos spin server macro

* leptos spin

* git chng

* based on the fermyon official git for when that works
2024-02-15 14:37:19 -08:00
Joseph Cruz
cd70b2f52b fix(ci): should exclude cargo-make 2024-02-11 20:40:20 -05:00
Joseph Cruz
c75842ed0c ci(hackernews_islands_axum): build with cargo leptos 2024-02-11 15:40:32 -05:00
Joseph Cruz
4ad228bf47 docs(test-report): add leptos ci warning 2024-02-11 15:40:32 -05:00
Joseph Cruz
bbe7115360 docs(test-report): mention options 2024-02-11 15:40:32 -05:00
Greg Johnston
0658a550b0 fix(examples): align crate name and output name (closes #2206) (#2291) 2024-02-10 15:47:25 -05:00
Joseph Cruz
4222c832b1 fix(ci): empty directory vector error (#2288)
* fix(ci): empty directory vector error

* chore(ci): simulate example change

* chore(ci): remove simulated example change
2024-02-10 10:02:21 -08:00
Greg Johnston
dfddbd6bf9 docs: give a warning when you try to .dispatch() an action immediately (closes #2225) (#2286) 2024-02-09 20:55:10 -05:00
Greg Johnston
8a77691cb5 Merge pull request #2285 from leptos-rs/fix-issues
Fix remaining CI issues
2024-02-09 19:29:01 -05:00
Joseph Cruz
c00207aa46 fix(test-report) should show all cargo-make leptos configuration (#2282)
* refactor(test-report): extract script
* refactor(test-report): extract functions
* refactor(test-report): split option to tasks
* chore(test-report): highlight examples without tags
* fix(test-report): show all cargo-make leptos configuration
* docs(test-report): update keys
* chore(test-report): include all crates in report
2024-02-09 16:31:46 -05:00
161 changed files with 2601 additions and 1660 deletions

View File

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

View File

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

28
.github/workflows/ci-semver.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: CI semver
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
get-leptos-changed:
uses: ./.github/workflows/get-leptos-changed.yml
test:
needs: [get-leptos-changed]
if: needs.get-leptos-changed.outputs.leptos_changed == 'true'
name: Run semver check (nightly-2024-03-31)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Sember Checks
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
rust-toolchain: nightly-2024-03-31

View File

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

View File

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

View File

@@ -21,12 +21,12 @@ jobs:
- name: Get example files that changed
id: changed-files
uses: tj-actions/changed-files@v41
uses: tj-actions/changed-files@v43
with:
files: |
examples/**
!examples/cargo-make
!examples/gtk
!examples/cargo-make/**
!examples/gtk/**
!examples/Makefile.toml
!examples/*.md

View File

@@ -17,8 +17,8 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install JQ Tool
uses: mbround18/install-jq@v1
- name: Install jq
run: sudo apt-get install jq
- name: Set Matrix
id: set-matrix

View File

@@ -19,7 +19,7 @@ jobs:
- name: Get source files that changed
id: changed-source
uses: tj-actions/changed-files@v41
uses: tj-actions/changed-files@v43
with:
files: |
integrations/**

View File

@@ -27,11 +27,9 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
override: true
components: rustfmt
- name: Add wasm32-unknown-unknown
run: rustup target add wasm32-unknown-unknown
@@ -44,6 +42,18 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Install binstall
uses: cargo-bins/cargo-binstall@main
- name: Install wasm-bindgen
run: cargo binstall wasm-bindgen-cli --no-confirm
- name: Install wasm-pack
run: cargo binstall wasm-pack --no-confirm
- name: Install cargo-leptos
run: cargo binstall cargo-leptos --no-confirm
- name: Install Trunk
uses: jetli/trunk-action@v0.4.0
with:
@@ -55,9 +65,9 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v3
name: Install pnpm
id: pnpm-install
with:
@@ -69,7 +79,7 @@ jobs:
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
@@ -107,6 +117,11 @@ jobs:
fi
done
- name: Install Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
# Run Cargo Make Task
- name: ${{ inputs.cargo_make_task }}
run: |

View File

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

View File

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

View File

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

View File

@@ -8,4 +8,11 @@ args = ["fmt", "--", "--check", "--config-path", "${LEPTOS_PROJECT_DIRECTORY}"]
[tasks.clippy-each-feature]
dependencies = ["install-clippy"]
command = "cargo"
args = ["hack", "clippy", "--all", "--each-feature", "--no-dev-deps"]
args = [
"clippy",
"--all-features",
"--no-deps",
"--",
"-D",
"clippy::print_stdout",
]

View File

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

View File

@@ -5,6 +5,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = ""
CARGO_MAKE_WORKSPACE_EMULATION = true
CARGO_MAKE_CRATE_WORKSPACE_MEMBERS = [
"action-form-error-handling",
"animated_show",
"counter",
"counter_isomorphic",
@@ -12,27 +13,33 @@ CARGO_MAKE_CRATE_WORKSPACE_MEMBERS = [
"counters_stable",
"counter_url_query",
"counter_without_macros",
"directives",
"error_boundary",
"errors_axum",
"fetch",
"hackernews",
"hackernews_axum",
"hackernews_islands_axum",
"hackernews_js_fetch",
"js-framework-benchmark",
"login_with_token_csr_only",
"parent_child",
"portal",
"router",
"server_fns_axum",
"session_auth_axum",
"slots",
"sso_auth_axum",
"ssr_modes",
"ssr_modes_axum",
"suspense_tests",
"tailwind_actix",
"tailwind_csr",
"tailwind_axum",
"tailwind_csr",
"timer",
"todo_app_sqlite",
"todo_app_sqlite_axum",
"todo_app_sqlite_csr",
"todomvc",
]
@@ -51,103 +58,5 @@ echo "CARGO_MAKE_CRATE_WORKSPACE_MEMBERS = $examples"
[tasks.test-report]
workspace = false
description = "report web testing technology used by examples - OPTION: [all]"
script = '''
set -emu
BOLD="\e[1m"
GREEN="\e[0;32m"
ITALIC="\e[3m"
YELLOW="\e[0;33m"
RESET="\e[0m"
echo
echo "${YELLOW}Web Test Technology${RESET}"
echo
makefile_paths=$(find . -name Makefile.toml -not -path '*/target/*' -not -path '*/node_modules/*' |
sed 's%./%%' |
sed 's%/Makefile.toml%%' |
grep -v Makefile.toml |
sort -u)
start_path=$(pwd)
for path in $makefile_paths; do
cd $path
crate_symbols=
pw_count=$(find . -name playwright.config.ts | wc -l)
while read -r line; do
case $line in
*"cucumber"*)
crate_symbols=$crate_symbols"C"
;;
*"fantoccini"*)
crate_symbols=$crate_symbols"D"
;;
esac
done <"./Cargo.toml"
while read -r line; do
case $line in
*"cargo-make/wasm-test.toml"*)
crate_symbols=$crate_symbols"W"
;;
*"cargo-make/playwright-test.toml"*)
crate_symbols=$crate_symbols"P"
crate_symbols=$crate_symbols"N"
;;
*"cargo-make/playwright-trunk-test.toml"*)
crate_symbols=$crate_symbols"P"
crate_symbols=$crate_symbols"T"
;;
*"cargo-make/trunk_server.toml"*)
crate_symbols=$crate_symbols"T"
;;
*"cargo-make/cargo-leptos-webdriver-test.toml"*)
crate_symbols=$crate_symbols"L"
;;
*"cargo-make/cargo-leptos-test.toml"*)
crate_symbols=$crate_symbols"L"
if [ $pw_count -gt 0 ]; then
crate_symbols=$crate_symbols"P"
fi
;;
esac
done <"./Makefile.toml"
# Sort list of tools
sorted_crate_symbols=$(echo ${crate_symbols} | grep -o . | sort | tr -d "\n")
formatted_crate_symbols="${BOLD}${YELLOW}${sorted_crate_symbols}${RESET}"
crate_line=$path
if [ ! -z ${1+x} ]; then
# Show all examples
if [ ! -z $crate_symbols ]; then
crate_line=$crate_line$formatted_crate_symbols
fi
echo $crate_line
elif [ ! -z $crate_symbols ]; then
# Filter out examples that do not run tests in `ci`
crate_line=$crate_line$formatted_crate_symbols
echo $crate_line
fi
cd ${start_path}
done
c="${BOLD}${YELLOW}C${RESET} = Cucumber"
d="${BOLD}${YELLOW}D${RESET} = WebDriver"
l="${BOLD}${YELLOW}L${RESET} = Cargo Leptos"
n="${BOLD}${YELLOW}N${RESET} = Node"
p="${BOLD}${YELLOW}P${RESET} = Playwright"
t="${BOLD}${YELLOW}T${RESET} = Trunk"
w="${BOLD}${YELLOW}W${RESET} = WASM"
echo
echo "${ITALIC}Keys:${RESET} $c, $d, $l, $n, $p, $t, $w"
echo
'''
description = "show the cargo-make configuration for web examples [web|all|help]"
script = { file = "./cargo-make/scripts/web-report.sh" }

View File

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

View File

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

View File

@@ -4,11 +4,13 @@ dependencies = [
"clean-trunk",
"clean-node_modules",
"clean-playwright",
"clean-pkg",
]
[tasks.clean-cargo]
command = "rm"
args = ["-rf", "target"]
script = '''
find . -type d -name target | xargs rm -rf
'''
[tasks.clean-trunk]
script = '''
@@ -27,3 +29,8 @@ fi
script = '''
find . -name playwright-report -name playwright -name test-results | xargs rm -rf
'''
[tasks.clean-pkg]
script = '''
find . -type d -name pkg | xargs rm -rf
'''

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ leptos_router = { path = "../../router" }
log = "0.4"
once_cell = "1.18"
gloo-net = { git = "https://github.com/rustwasm/gloo" }
wasm-bindgen = "0.2.87"
wasm-bindgen = "0.2"
serde = { version = "1", features = ["derive"] }
simple_logger = "4.3"
tracing = { version = "0.1", optional = true }

View File

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

View File

@@ -2,6 +2,7 @@
name = "counter_without_macros"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
[profile.release]
codegen-units = 1
@@ -14,7 +15,7 @@ log = "0.4"
console_error_panic_hook = "0.1.7"
[dev-dependencies]
wasm-bindgen = "0.2.84"
wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.34"
pretty_assertions = "1.3.0"
rstest = "0.17.0"

View File

@@ -1,4 +1,4 @@
use leptos::{ev, html::*, *};
use leptos::{html::*, *};
/// A simple counter view.
// A component is really just a function call: it runs once to create the DOM and reactive system

View File

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

View File

@@ -1,4 +1,4 @@
use leptos::{For, *};
use leptos::*;
const MANY_COUNTERS: usize = 1000;

View File

@@ -2,6 +2,7 @@
name = "counters_stable"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
[dependencies]
leptos = { path = "../../leptos", features = ["csr"] }
@@ -11,7 +12,7 @@ console_log = "1"
console_error_panic_hook = "0.1.7"
[dev-dependencies]
wasm-bindgen = "0.2.87"
wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.37"
pretty_assertions = "1.4.0"

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
use crate::errors::AppError;
use leptos::{logging::log, Errors, *};
use leptos::{logging::log, *};
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
use leptos::{ev, *};
use leptos::*;
#[component]
pub fn CredentialsForm(

View File

@@ -16,7 +16,7 @@ impl AppState {
credentials: Credentials,
) -> Result<(), CreateUserError> {
let Credentials { email, password } = credentials;
let user_exists = self.users.get(&email).is_some();
let user_exists = self.users.contains_key(&email);
if user_exists {
return Err(CreateUserError::UserExists);
}
@@ -124,6 +124,7 @@ impl EmailAddress {
#[derive(Clone)]
pub struct CurrentUser {
pub email: EmailAddress,
#[allow(dead_code)] // possibly a lint regression, this is used at line 65
pub token: Uuid,
}

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
CREATE TABLE IF NOT EXISTS todos
(
id INTEGER NOT NULL PRIMARY KEY,
title VARCHAR,
completed BOOLEAN
);

View File

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

View File

@@ -1,5 +1,5 @@
use crate::errors::TodoAppError;
use leptos::{Errors, *};
use leptos::*;
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;

View File

@@ -30,10 +30,10 @@ sqlx = { version = "0.7.2", features = [
], optional = true }
thiserror = "1.0"
wasm-bindgen = "0.2"
axum_session_auth = { version = "0.10", features = [
axum_session_auth = { version = "0.12.1", features = [
"sqlite-rustls",
], optional = true }
axum_session = { version = "0.10", features = [
axum_session = { version = "0.12.4", features = [
"sqlite-rustls",
], optional = true }
bcrypt = { version = "0.15", optional = true }

View File

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

View File

@@ -6,10 +6,13 @@ use std::collections::HashSet;
pub struct User {
pub id: i64,
pub username: String,
pub password: String,
pub permissions: HashSet<String>,
}
// Explicitly is not Serialize/Deserialize!
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UserPasshash(String);
impl Default for User {
fn default() -> Self {
let permissions = HashSet::new();
@@ -17,7 +20,6 @@ impl Default for User {
Self {
id: -1,
username: "Guest".into(),
password: "".into(),
permissions,
}
}
@@ -25,7 +27,7 @@ impl Default for User {
#[cfg(feature = "ssr")]
pub mod ssr {
pub use super::User;
pub use super::{User, UserPasshash};
pub use axum_session_auth::{
Authentication, HasPermission, SessionSqlitePool,
};
@@ -42,7 +44,10 @@ pub mod ssr {
pub use bcrypt::{hash, verify, DEFAULT_COST};
impl User {
pub async fn get(id: i64, pool: &SqlitePool) -> Option<Self> {
pub async fn get_with_passhash(
id: i64,
pool: &SqlitePool,
) -> Option<(Self, UserPasshash)> {
let sqluser = sqlx::query_as::<_, SqlUser>(
"SELECT * FROM users WHERE id = ?",
)
@@ -63,10 +68,16 @@ pub mod ssr {
Some(sqluser.into_user(Some(sql_user_perms)))
}
pub async fn get_from_username(
pub async fn get(id: i64, pool: &SqlitePool) -> Option<Self> {
User::get_with_passhash(id, pool)
.await
.map(|(user, _)| user)
}
pub async fn get_from_username_with_passhash(
name: String,
pool: &SqlitePool,
) -> Option<Self> {
) -> Option<(Self, UserPasshash)> {
let sqluser = sqlx::query_as::<_, SqlUser>(
"SELECT * FROM users WHERE username = ?",
)
@@ -86,6 +97,15 @@ pub mod ssr {
Some(sqluser.into_user(Some(sql_user_perms)))
}
pub async fn get_from_username(
name: String,
pool: &SqlitePool,
) -> Option<Self> {
User::get_from_username_with_passhash(name, pool)
.await
.map(|(user, _)| user)
}
}
#[derive(sqlx::FromRow, Clone)]
@@ -137,20 +157,22 @@ pub mod ssr {
pub fn into_user(
self,
sql_user_perms: Option<Vec<SqlPermissionTokens>>,
) -> User {
User {
id: self.id,
username: self.username,
password: self.password,
permissions: if let Some(user_perms) = sql_user_perms {
user_perms
.into_iter()
.map(|x| x.token)
.collect::<HashSet<String>>()
} else {
HashSet::<String>::new()
) -> (User, UserPasshash) {
(
User {
id: self.id,
username: self.username,
permissions: if let Some(user_perms) = sql_user_perms {
user_perms
.into_iter()
.map(|x| x.token)
.collect::<HashSet<String>>()
} else {
HashSet::<String>::new()
},
},
}
UserPasshash(self.password),
)
}
}
}
@@ -180,11 +202,12 @@ pub async fn login(
let pool = pool()?;
let auth = auth()?;
let user: User = User::get_from_username(username, &pool)
.await
.ok_or_else(|| ServerFnError::new("User does not exist."))?;
let (user, UserPasshash(expected_passhash)) =
User::get_from_username_with_passhash(username, &pool)
.await
.ok_or_else(|| ServerFnError::new("User does not exist."))?;
match verify(password, &user.password)? {
match verify(password, &expected_passhash)? {
true => {
auth.login_user(user.id);
auth.remember_user(remember.is_some());

View File

@@ -1,5 +1,5 @@
use crate::errors::TodoAppError;
use leptos::{Errors, *};
use leptos::*;
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;

View File

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

View File

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

View File

@@ -8,12 +8,12 @@ use axum::{
Router,
};
use axum_session::{Key, SessionConfig, SessionLayer, SessionStore};
use axum_session_auth::{AuthConfig, AuthSessionLayer, SessionSqlitePool};
use axum_session_auth::{AuthConfig, AuthSessionLayer};
use leptos::{get_configuration, logging::log, provide_context, view};
use leptos_axum::{
generate_route_list, handle_server_fns_with_context, LeptosRoutes,
};
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
use sqlx::sqlite::SqlitePoolOptions;
use sso_auth_axum::{
auth::*, fallback::file_and_error_handler, state::AppState,
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_router = { path = "../../router" }
log = "0.4"
simple_logger = "4"
wasm-bindgen = "0.2.87"
wasm-bindgen = "0.2"
serde = "1.0.159"
tokio = { version = "1.29", features = ["time", "rt"], optional = true }

View File

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

View File

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

View File

@@ -1,926 +0,0 @@
{
"name": "leptos-tailwind",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "leptos-tailwind",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"preline": "^1.8.0",
"tailwindcss": "^3.3.2"
}
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.18",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
"dependencies": {
"@jridgewell/resolve-uri": "3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"engines": {
"node": ">=8"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"engines": {
"node": ">= 6"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"engines": {
"node": ">= 6"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"bin": {
"cssesc": "bin/cssesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
},
"engines": {
"node": ">=8.6.0"
}
},
"node_modules/fast-glob/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"dependencies": {
"reusify": "^1.0.4"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dependencies": {
"is-glob": "^4.0.3"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
"integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/jiti": {
"version": "1.18.2",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
"bin": {
"jiti": "bin/jiti.js"
}
},
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"engines": {
"node": ">= 8"
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"engines": {
"node": ">= 6"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pirates": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
"integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
"engines": {
"node": ">= 6"
}
},
"node_modules/postcss": {
"version": "8.4.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
"integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/postcss-js": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dependencies": {
"camelcase-css": "^2.0.1"
},
"engines": {
"node": "^12 || ^14 || >= 16"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": {
"postcss": "^8.4.21"
}
},
"node_modules/postcss-load-config": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
"dependencies": {
"lilconfig": "^2.0.5",
"yaml": "^2.1.1"
},
"engines": {
"node": ">= 14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": {
"postcss": ">=8.0.9",
"ts-node": ">=9.0.0"
},
"peerDependenciesMeta": {
"postcss": {
"optional": true
},
"ts-node": {
"optional": true
}
}
},
"node_modules/postcss-nested": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
"dependencies": {
"postcss-selector-parser": "^6.0.11"
},
"engines": {
"node": ">=12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
},
"node_modules/postcss-selector-parser": {
"version": "6.0.13",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
"integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/preline": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/preline/-/preline-1.8.0.tgz",
"integrity": "sha512-guttn86Fc/+AbvN9oKcr2z3zU7DL3Q5dl7nhcR4nTi5F02LXQc7WIYwgIXMR97kymCs52feiju6glXO3dUIpvA==",
"dependencies": {
"@popperjs/core": "^2.11.2"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dependencies": {
"pify": "^2.3.0"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.2",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
"dependencies": {
"is-core-module": "^2.11.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"queue-microtask": "^1.2.2"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sucrase": {
"version": "3.32.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
"glob": "7.1.6",
"lines-and-columns": "^1.1.6",
"mz": "^2.7.0",
"pirates": "^4.0.1",
"ts-interface-checker": "^0.1.9"
},
"bin": {
"sucrase": "bin/sucrase",
"sucrase-node": "bin/sucrase-node"
},
"engines": {
"node": ">=8"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tailwindcss": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.5.3",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.2.12",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.18.2",
"lilconfig": "^2.1.0",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.0.0",
"postcss": "^8.4.23",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.1",
"postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.11",
"postcss-value-parser": "^4.2.0",
"resolve": "^1.22.2",
"sucrase": "^3.32.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dependencies": {
"any-promise": "^1.0.0"
}
},
"node_modules/thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/yaml": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
"engines": {
"node": ">= 14"
}
}
}
}

View File

@@ -4,13 +4,13 @@
"description": "<picture>\r <source srcset=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg\" media=\"(prefers-color-scheme: dark)\">\r <img src=\"https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg\" alt=\"Leptos Logo\">\r </picture>",
"main": "index.js",
"scripts": {
"build":"npx tailwindcss -i ./input.css -o ./style/output.css",
"watch":"npx tailwindcss -i ./input.css -o ./style/output.css --watch"
"build": "npx tailwindcss -i ./input.css -o ./style/output.css",
"watch": "npx tailwindcss -i ./input.css -o ./style/output.css --watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"tailwindcss": "^3.3.2"
"tailwindcss": "^3.4.1"
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
use crate::errors::TodoAppError;
use leptos::{Errors, *};
use leptos::*;
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;

View File

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

View File

@@ -1,5 +1,5 @@
use crate::errors::TodoAppError;
use leptos::{Errors, *};
use leptos::*;
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;

View File

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

View File

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

View File

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

View File

@@ -203,7 +203,7 @@ pub fn handle_server_fns() -> Route {
/// context, allowing you to pass in info about the route or user from Actix, or other info.
///
/// **NOTE**: If your server functions expect a context, make sure to provide it both in
/// [`handle_server_fns_with_context`] **and** in [`leptos_routes_with_context`] (or whatever
/// [`handle_server_fns_with_context`] **and** in [`LeptosRoutes::leptos_routes_with_context`] (or whatever
/// rendering method you are using). During SSR, server functions are called by the rendering
/// method, while subsequent calls from the client are handled by the server function handler.
/// The same context needs to be provided to both handlers.
@@ -1210,18 +1210,32 @@ where
IV: IntoView + 'static,
{
let mut router = self;
// register server functions first to allow for wildcard route in Leptos's Router
for (path, _) in server_fn::actix::server_fn_paths() {
let additional_context = additional_context.clone();
let handler = handle_server_fns_with_context(additional_context);
router = router.route(path, handler);
}
// register routes defined in Leptos's Router
for listing in paths.iter() {
let path = listing.path();
let mode = listing.mode();
for method in listing.methods() {
let additional_context = additional_context.clone();
let additional_context_and_method = move || {
provide_context(method);
additional_context();
};
router = if let Some(static_mode) = listing.static_mode() {
router.route(
path,
static_route(
options.clone(),
app_fn.clone(),
additional_context.clone(),
additional_context_and_method.clone(),
method,
static_mode,
),
@@ -1233,7 +1247,7 @@ where
SsrMode::OutOfOrder => {
render_app_to_stream_with_context(
options.clone(),
additional_context.clone(),
additional_context_and_method.clone(),
app_fn.clone(),
method,
)
@@ -1241,7 +1255,7 @@ where
SsrMode::PartiallyBlocked => {
render_app_to_stream_with_context_and_replace_blocks(
options.clone(),
additional_context.clone(),
additional_context_and_method.clone(),
app_fn.clone(),
method,
true,
@@ -1250,14 +1264,14 @@ where
SsrMode::InOrder => {
render_app_to_stream_in_order_with_context(
options.clone(),
additional_context.clone(),
additional_context_and_method.clone(),
app_fn.clone(),
method,
)
}
SsrMode::Async => render_app_async_with_context(
options.clone(),
additional_context.clone(),
additional_context_and_method.clone(),
app_fn.clone(),
method,
),
@@ -1267,13 +1281,6 @@ where
}
}
// register server functions
for (path, _) in server_fn::actix::server_fn_paths() {
let additional_context = additional_context.clone();
let handler = handle_server_fns_with_context(additional_context);
router = router.route(path, handler);
}
router
}
}
@@ -1306,6 +1313,15 @@ impl LeptosRoutes for &mut ServiceConfig {
IV: IntoView + 'static,
{
let mut router = self;
// register server functions first to allow for wildcard route in Leptos's Router
for (path, _) in server_fn::actix::server_fn_paths() {
let additional_context = additional_context.clone();
let handler = handle_server_fns_with_context(additional_context);
router = router.route(path, handler);
}
// register routes defined in Leptos's Router
for listing in paths.iter() {
let path = listing.path();
let mode = listing.mode();
@@ -1350,13 +1366,6 @@ impl LeptosRoutes for &mut ServiceConfig {
}
}
// register server functions
for (path, _) in server_fn::actix::server_fn_paths() {
let additional_context = additional_context.clone();
let handler = handle_server_fns_with_context(additional_context);
router = router.route(path, handler);
}
router
}
}

View File

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

View File

@@ -6,6 +6,7 @@ authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Axum integrations for the Leptos web framework."
rust-version.workspace = true
[dependencies]
axum = { version = "0.7", default-features = false, features = [

View File

@@ -79,6 +79,20 @@ impl ResponseParts {
}
/// Allows you to override details of the HTTP response like the status code and add Headers/Cookies.
///
/// `ResponseOptions` is provided via context when you use most of the handlers provided in this
/// crate, including [`.leptos_routes`](LeptosRoutes::leptos_routes),
/// [`.leptos_routes_with_context`](LeptosRoutes::leptos_routes_with_context), [`handle_server_fns`], etc.
/// You can find the full set of provided context types in each handler function.
///
/// If you provide your own handler, you will need to provide `ResponseOptions` via context
/// yourself if you want to access it via context.
/// ```rust,ignore
/// #[server]
/// pub async fn get_opts() -> Result<(), ServerFnError> {
/// let opts = expect_context::<leptos_axum::ResponseOptions>();
/// Ok(())
/// }
#[derive(Debug, Clone, Default)]
pub struct ResponseOptions(pub Arc<RwLock<ResponseParts>>);
@@ -257,7 +271,13 @@ async fn handle_server_fns_inner(
let (tx, rx) = futures::channel::oneshot::channel();
// capture current span to enable trace context propagation
let current_span = tracing::Span::current();
spawn_task!(async move {
// enter captured span for trace context propagation in spawned task
let _guard = current_span.enter();
let path = req.uri().path().to_string();
let (req, parts) = generate_request_and_parts(req);
@@ -282,14 +302,8 @@ async fn handle_server_fns_inner(
// actually run the server fn
let mut res = service.run(req).await;
// update response as needed
let res_options = expect_context::<ResponseOptions>().0;
let res_options_inner = res_options.read();
let (status, mut res_headers) =
(res_options_inner.status, res_options_inner.headers.clone());
// it it accepts text/html (i.e., is a plain form post) and doesn't already have a
// Location set, then redirect to to Referer
// if it accepts text/html (i.e., is a plain form post) and doesn't already have a
// Location set, then redirect to Referer
if accepts_html {
if let Some(referrer) = referrer {
let has_location = res.headers().get(LOCATION).is_some();
@@ -300,11 +314,22 @@ async fn handle_server_fns_inner(
}
}
// apply status code and headers if used changed them
if let Some(status) = status {
*res.status_mut() = status;
// update response as needed
if let Some(res_options) = use_context::<ResponseOptions>() {
let res_options_inner = res_options.0.read();
let (status, mut res_headers) = (
res_options_inner.status,
res_options_inner.headers.clone(),
);
// apply status code and headers if used changed them
if let Some(status) = status {
*res.status_mut() = status;
}
res.headers_mut().extend(res_headers.drain());
} else {
eprintln!("Failed to find ResponseOptions for {path}");
}
res.headers_mut().extend(res_headers.drain());
// clean up the scope
runtime.dispose();
@@ -1055,7 +1080,7 @@ where
headers.extend(res_headers.drain());
// This one doesn't use generate_response(), so we need to do this seperately
// This one doesn't use generate_response(), so we need to do this separately
if !headers.contains_key(header::CONTENT_TYPE) {
// Set the Content Type headers on all responses. This makes Firefox show the page source
// without complaining
@@ -1604,100 +1629,7 @@ where
let mut router = self;
// register router paths
for listing in paths.iter() {
let path = listing.path();
for method in listing.methods() {
router = if let Some(static_mode) = listing.static_mode() {
#[cfg(feature = "default")]
{
static_route(
router,
path,
LeptosOptions::from_ref(options),
app_fn.clone(),
cx_with_state.clone(),
method,
static_mode,
)
}
#[cfg(not(feature = "default"))]
{
_ = static_mode;
panic!(
"Static site generation is not currently \
supported on WASM32 server targets."
)
}
} else {
router.route(
path,
match listing.mode() {
SsrMode::OutOfOrder => {
let s = render_app_to_stream_with_context(
LeptosOptions::from_ref(options),
cx_with_state.clone(),
app_fn.clone(),
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
SsrMode::PartiallyBlocked => {
let s = render_app_to_stream_with_context_and_replace_blocks(
LeptosOptions::from_ref(options),
cx_with_state.clone(),
app_fn.clone(),
true
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
SsrMode::InOrder => {
let s = render_app_to_stream_in_order_with_context(
LeptosOptions::from_ref(options),
cx_with_state.clone(),
app_fn.clone(),
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
SsrMode::Async => {
let s = render_app_async_with_context(
LeptosOptions::from_ref(options),
cx_with_state.clone(),
app_fn.clone(),
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
},
)
};
}
}
// register server functions
// register server functions first to allow for wildcard router path
for (path, method) in server_fn::axum::server_fn_paths() {
let cx_with_state = cx_with_state.clone();
let handler = move |req: Request<Body>| async move {
@@ -1721,6 +1653,104 @@ where
);
}
// register router paths
for listing in paths.iter() {
let path = listing.path();
for method in listing.methods() {
let cx_with_state = cx_with_state.clone();
let cx_with_state_and_method = move || {
provide_context(method);
cx_with_state();
};
router = if let Some(static_mode) = listing.static_mode() {
#[cfg(feature = "default")]
{
static_route(
router,
path,
LeptosOptions::from_ref(options),
app_fn.clone(),
cx_with_state_and_method.clone(),
method,
static_mode,
)
}
#[cfg(not(feature = "default"))]
{
_ = static_mode;
panic!(
"Static site generation is not currently \
supported on WASM32 server targets."
)
}
} else {
router.route(
path,
match listing.mode() {
SsrMode::OutOfOrder => {
let s = render_app_to_stream_with_context(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
app_fn.clone(),
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
SsrMode::PartiallyBlocked => {
let s = render_app_to_stream_with_context_and_replace_blocks(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
app_fn.clone(),
true
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
SsrMode::InOrder => {
let s = render_app_to_stream_in_order_with_context(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
app_fn.clone(),
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
SsrMode::Async => {
let s = render_app_async_with_context(
LeptosOptions::from_ref(options),
cx_with_state_and_method.clone(),
app_fn.clone(),
);
match method {
leptos_router::Method::Get => get(s),
leptos_router::Method::Post => post(s),
leptos_router::Method::Put => put(s),
leptos_router::Method::Delete => delete(s),
leptos_router::Method::Patch => patch(s),
}
}
},
)
};
}
}
router
}

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces."
readme = "../README.md"
rust-version.workspace = true
[dependencies]
cfg-if = "1"
@@ -15,12 +16,18 @@ leptos_macro = { workspace = true }
leptos_reactive = { workspace = true }
leptos_server = { workspace = true }
leptos_config = { workspace = true }
leptos-spin-macro = { version = "0.1", optional = true }
tracing = "0.1"
typed-builder = "0.18"
typed-builder-macro = "0.18"
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
server_fn = { workspace = true, features = ["form-redirects", "browser", "url", "cbor"] }
server_fn = { workspace = true, features = [
"form-redirects",
"browser",
"url",
"cbor",
] }
web-sys = { version = "0.3.63", features = [
"ShadowRoot",
"ShadowRootInit",
@@ -66,7 +73,7 @@ miniserde = ["leptos_reactive/miniserde"]
rkyv = ["leptos_reactive/rkyv"]
tracing = ["leptos_macro/tracing"]
nonce = ["leptos_dom/nonce"]
spin = ["leptos_reactive/spin"]
spin = ["leptos_reactive/spin", "leptos-spin-macro"]
experimental-islands = [
"leptos_dom/experimental-islands",
"leptos_macro/experimental-islands",
@@ -76,7 +83,7 @@ experimental-islands = [
]
trace-component-props = [
"leptos_dom/trace-component-props",
"leptos_macro/trace-component-props"
"leptos_macro/trace-component-props",
]
[package.metadata.cargo-all-features]
@@ -87,7 +94,10 @@ denylist = [
"rustls",
"default-tls",
"wasm-bindgen",
"trace-component-props"
"rkyv", # was causing clippy issues on nightly
"trace-component-props",
"spin",
"experimental-islands",
]
skip_feature_sets = [
[

View File

@@ -1,10 +1,7 @@
use crate::Children;
use leptos_dom::{Errors, HydrationCtx, IntoView};
use leptos_macro::{component, view};
use leptos_reactive::{
create_rw_signal, provide_context, run_as_child, signal_prelude::*,
RwSignal,
};
use leptos_reactive::{provide_context, run_as_child, signal_prelude::*};
/// When you render a `Result<_, _>` in your view, in the `Err` case it will
/// render nothing, and search up through the view tree for an `<ErrorBoundary/>`.

View File

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

View File

@@ -1,6 +1,6 @@
use leptos::{component, ChildrenFn, ViewFn};
use leptos_dom::IntoView;
use leptos_reactive::{create_memo, signal_prelude::*};
use leptos_reactive::signal_prelude::*;
/// A component that will show its children when the `when` condition is `true`,
/// and show the fallback when it is `false`, without rerendering every time

View File

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

View File

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

View File

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

View File

@@ -7,9 +7,10 @@ license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "Configuration for the Leptos web framework."
readme = "../README.md"
rust-version.workspace = true
[dependencies]
config = { version = "0.13.3", default-features = false, features = ["toml"] }
config = { version = "0.14", default-features = false, features = ["toml", "convert-case"] }
regex = "1.7.0"
serde = { version = "1.0.151", features = ["derive"] }
thiserror = "1.0.38"
@@ -18,3 +19,4 @@ typed-builder = "0.18"
[dev-dependencies]
tokio = { version = "1", features = ["rt", "macros"] }
tempfile = "3"
temp-env = { version = "0.3.6", features = ["async_closure"] }

View File

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

View File

@@ -3,12 +3,9 @@
pub mod errors;
use crate::errors::LeptosConfigError;
use config::{Config, File, FileFormat};
use config::{Case, Config, File, FileFormat};
use regex::Regex;
use std::{
convert::TryFrom, env::VarError, fs, net::SocketAddr, path::Path,
str::FromStr,
};
use std::{env::VarError, fs, net::SocketAddr, path::Path, str::FromStr};
use typed_builder::TypedBuilder;
/// A Struct to allow us to parse LeptosOptions from the file. Not really needed, most interactions should
@@ -70,6 +67,15 @@ pub struct LeptosOptions {
#[builder(default = default_not_found_path())]
#[serde(default = "default_not_found_path")]
pub not_found_path: String,
/// The file name of the hash text file generated by cargo-leptos. Defaults to `hash.txt`.
#[builder(default = default_hash_file_name())]
#[serde(default = "default_hash_file_name")]
pub hash_file: String,
/// If true, hashes will be generated for all files in the site_root and added to their file names.
/// Defaults to `true`.
#[builder(default = default_hash_files())]
#[serde(default = "default_hash_files")]
pub hash_files: bool,
}
impl LeptosOptions {
@@ -108,6 +114,8 @@ impl LeptosOptions {
env_w_default("LEPTOS_RELOAD_WS_PROTOCOL", "ws")?.as_str(),
)?,
not_found_path: env_w_default("LEPTOS_NOT_FOUND_PATH", "/404")?,
hash_file: env_w_default("LEPTOS_HASH_FILE_NAME", "hash.txt")?,
hash_files: env_w_default("LEPTOS_HASH_FILES", "false")?.parse()?,
})
}
}
@@ -146,6 +154,14 @@ fn default_not_found_path() -> String {
"/404".to_string()
}
fn default_hash_file_name() -> String {
"hash.txt".to_string()
}
fn default_hash_files() -> bool {
false
}
fn env_wo_default(key: &str) -> Result<Option<String>, LeptosConfigError> {
match std::env::var(key) {
Ok(val) => Ok(Some(val)),
@@ -281,7 +297,9 @@ impl TryFrom<String> for ReloadWSProtocol {
/// Loads [LeptosOptions] from a Cargo.toml text content with layered overrides.
/// If an env var is specified, like `LEPTOS_ENV`, it will override a setting in the file.
pub fn get_config_from_str(text: &str) -> Result<ConfFile, LeptosConfigError> {
pub fn get_config_from_str(
text: &str,
) -> Result<LeptosOptions, LeptosConfigError> {
let re: Regex = Regex::new(r"(?m)^\[package.metadata.leptos\]").unwrap();
let re_workspace: Regex =
Regex::new(r"(?m)^\[\[workspace.metadata.leptos\]\]").unwrap();
@@ -305,14 +323,18 @@ pub fn get_config_from_str(text: &str) -> Result<ConfFile, LeptosConfigError> {
// so that serde error messages have right line number
let newlines = text[..start].matches('\n').count();
let input = "\n".repeat(newlines) + &text[start..];
let toml = input.replace(metadata_name, "[leptos-options]");
// so the settings will be interpreted as root level settings
let toml = input.replace(metadata_name, "");
let settings = Config::builder()
// Read the "default" configuration file
.add_source(File::from_str(&toml, FileFormat::Toml))
// Layer on the environment-specific values.
// Add in settings from environment variables (with a prefix of LEPTOS and '_' as separator)
// Add in settings from environment variables (with a prefix of LEPTOS)
// E.g. `LEPTOS_RELOAD_PORT=5001 would set `LeptosOptions.reload_port`
.add_source(config::Environment::with_prefix("LEPTOS").separator("_"))
.add_source(
config::Environment::with_prefix("LEPTOS")
.convert_case(Case::Kebab),
)
.build()?;
settings
@@ -342,7 +364,8 @@ pub async fn get_config_from_file<P: AsRef<Path>>(
) -> Result<ConfFile, LeptosConfigError> {
let text = fs::read_to_string(path)
.map_err(|_| LeptosConfigError::ConfigNotFound)?;
get_config_from_str(&text)
let leptos_options = get_config_from_str(&text)?;
Ok(ConfFile { leptos_options })
}
/// Loads [LeptosOptions] from environment variables or rely on the defaults

View File

@@ -30,44 +30,53 @@ fn ws_from_str_test() {
#[test]
fn env_w_default_test() {
std::env::set_var("LEPTOS_CONFIG_ENV_TEST", "custom");
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("custom")
);
std::env::remove_var("LEPTOS_CONFIG_ENV_TEST");
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("default")
);
_ = temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("custom")
);
});
_ = temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("default")
);
});
}
#[test]
fn env_wo_default_test() {
std::env::set_var("LEPTOS_CONFIG_ENV_TEST", "custom");
assert_eq!(
env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(),
Some(String::from("custom"))
);
std::env::remove_var("LEPTOS_CONFIG_ENV_TEST");
assert_eq!(env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(), None);
_ = temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
assert_eq!(
env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(),
Some(String::from("custom"))
);
});
_ = temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
assert_eq!(env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(), None);
});
}
#[test]
fn try_from_env_test() {
// Test config values from environment variables
std::env::set_var("LEPTOS_OUTPUT_NAME", "app_test");
std::env::set_var("LEPTOS_SITE_ROOT", "my_target/site");
std::env::set_var("LEPTOS_SITE_PKG_DIR", "my_pkg");
std::env::set_var("LEPTOS_SITE_ADDR", "0.0.0.0:80");
std::env::set_var("LEPTOS_RELOAD_PORT", "8080");
std::env::set_var("LEPTOS_RELOAD_EXTERNAL_PORT", "8080");
std::env::set_var("LEPTOS_ENV", "PROD");
std::env::set_var("LEPTOS_RELOAD_WS_PROTOCOL", "WSS");
let config = temp_env::with_vars(
[
("LEPTOS_OUTPUT_NAME", Some("app_test")),
("LEPTOS_SITE_ROOT", Some("my_target/site")),
("LEPTOS_SITE_PKG_DIR", Some("my_pkg")),
("LEPTOS_SITE_ADDR", Some("0.0.0.0:80")),
("LEPTOS_RELOAD_PORT", Some("8080")),
("LEPTOS_RELOAD_EXTERNAL_PORT", Some("8080")),
("LEPTOS_ENV", Some("PROD")),
("LEPTOS_RELOAD_WS_PROTOCOL", Some("WSS")),
],
|| LeptosOptions::try_from_env().unwrap(),
);
let config = LeptosOptions::try_from_env().unwrap();
assert_eq!(config.output_name, "app_test");
assert_eq!(config.site_root, "my_target/site");
assert_eq!(config.site_pkg_dir, "my_pkg");
assert_eq!(

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