Compare commits

..

23 Commits

Author SHA1 Message Date
Greg Johnston
55ea00afdd Router sets status code 404 when it can't match a route and returns fallback 2023-01-16 20:50:48 -05:00
Greg Johnston
f7d5567a35 Fix fallback (signature and functionality) 2023-01-16 19:55:32 -05:00
Greg Johnston
61ca6465df Merge pull request #318 from benwis/main
Switch get_configuration calls to None and add a note in the docs for get_configuration()
2023-01-16 07:23:55 -05:00
benwis
10a833d763 Switch get_configuration calls to None and add a note in the docs for
uses of get_configuration()
2023-01-15 12:50:25 -08:00
Greg Johnston
17982e8ac5 Merge pull request #316 from leptos-rs/death-to-suspense-hydration-mismatches
Death to suspense hydration mismatches
2023-01-14 15:19:12 -05:00
Greg Johnston
6cfb6227f5 Merge pull request #315 from leptos-rs/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2023-01-14 14:14:29 -05:00
Greg Johnston
159852b8d7 clippy 2023-01-14 14:12:52 -05:00
Greg Johnston
6f95713b59 Fix <Suspense/> hydration 2023-01-14 14:10:19 -05:00
Greg Johnston
e17afd4559 Handle custom elements correctly 2023-01-14 14:09:23 -05:00
Greg Johnston
e0aa1e245b Create CODE_OF_CONDUCT.md 2023-01-14 12:04:11 -05:00
Greg Johnston
7951a6e9cf Merge pull request #314 from leptos-rs/create-slice-doctest
Fix doctest in `create_slice` and edit doc comment slightly
2023-01-14 09:50:29 -05:00
Greg Johnston
1f39299303 Fix doctest in create_slice and edit doc comment slightly 2023-01-14 08:17:27 -05:00
Greg Johnston
2be4610233 Update README.md 2023-01-14 08:03:57 -05:00
Greg Johnston
af254e9b61 Merge pull request #312 from TaKO8Ki/use-rust-cache
Use rust-cache in CI
2023-01-14 07:59:18 -05:00
Takayuki Maeda
4aae8a5088 use rust cache 2023-01-14 20:07:09 +09:00
Greg Johnston
7ff044cef6 Merge pull request #308 from Indrazar/main
Update Generated API URL on Windows Attempt #2
2023-01-13 07:30:03 -05:00
Greg Johnston
11122b575e Merge pull request #310 from akesson/doc-examples-fixes
Doc fix + search & replace url
2023-01-13 07:29:47 -05:00
Greg Johnston
a62ee4031b Merge branch 'main' into doc-examples-fixes 2023-01-13 07:29:42 -05:00
Greg Johnston
19b43607a1 Merge pull request #309 from dzfrias/patch-1 2023-01-13 07:27:46 -05:00
hakesson
884297706a Search https://github.com/gbj/ and replace with https://github.com/leptos-rs/ 2023-01-13 09:03:11 +01:00
hakesson
fb4c208609 Should point to counters (plural) 2023-01-13 09:00:35 +01:00
Diego Frias
5701e74efb Fix link to counters_stable in README 2023-01-12 20:26:11 -08:00
indrazar
2afe8e202a update url for Windows directories attempt 2 2023-01-12 22:07:55 -05:00
35 changed files with 284 additions and 145 deletions

View File

@@ -39,16 +39,7 @@ jobs:
- name: Run Rustfmt
run: cargo fmt -- --check
- name: Cargo cache
uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ matrix.rust }}-${{ hashFiles('**/Cargo.lock') }}
- uses: Swatinem/rust-cache@v2
- name: Run tests with all features
run: cargo make ci

51
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,51 @@
# Contributor Covenant Code of Conduct
_This Code of Conduct is based on the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)
and the [Bevy Code of Conduct](https://raw.githubusercontent.com/bevyengine/bevy/main/CODE_OF_CONDUCT.md),
which are adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling)
and the [Contributor Covenant](https://www.contributor-covenant.org)._
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
We are a community of people learning and exploring how to build better web applications
with Rust. When interacting with one another, please remember that there are no experts and there are
no stupid questions. Assume the best in other people's communication, and take a step back if
you find yourself getting defensive.
Please note the following guidelines as well:
* Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.
* Please be kind and courteous. Theres no need to be mean or rude.
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we dont tolerate behavior that excludes people in socially marginalized groups.
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact the maintainers immediately. Whether youre a regular contributor or a newcomer, we care about making this community a safe place for you and weve got your back.
* Do not make casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology.
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
## Moderation
These are the policies for upholding [our communitys standards of conduct](#our-standards). If you feel that a thread needs moderation, please contact the maintainers.
1. Remarks that violate the community standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner).
2. Remarks that maintainers find inappropriate, whether listed in the code of conduct or not, are also not allowed.
3. Maintainers will first respond to such remarks with a warning.
4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
6. Maintainers may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
7. If a maintainer bans someone and you think it was unjustified, please take it up with that maintainer, or with a different maintainer, in private. Complaints about bans in-channel are not allowed.
8. Maintainers are held to a higher standard than other community members. If a maintainer creates an inappropriate situation, they should expect less leeway than others.
The enforcement policies in the code of conduct apply to all official venues, including Discord channels, GitHub repositories, and all other forums.

View File

@@ -1,5 +1,3 @@
**NOTE: We're in the middle of merging changes and making fixes to support our upcoming `0.1.0` release. Some of the examples may be in a broken state. You can continue using the `0.0` releases with no issues.**
<picture>
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_pref_dark_RGB.svg" media="(prefers-color-scheme: dark)">
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
@@ -61,9 +59,9 @@ Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained re
Here are some resources for learning more about Leptos:
- [Examples](https://github.com/gbj/leptos/tree/main/examples)
- [Examples](https://github.com/leptos-rs/leptos/tree/main/examples)
- [API Documentation](https://docs.rs/leptos/latest/leptos/)
- [Common Bugs](https://github.com/gbj/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
- [Common Bugs](https://github.com/leptos-rs/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
- Leptos Guide (in progress)
## `nightly` Note
@@ -82,13 +80,13 @@ If youre on `stable`, note the following:
1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.1.0-alpha", features = ["stable"] }`
2. `nightly` enables the function call syntax for accessing and setting signals. If youre using `stable`,
youll just call `.get()`, `.set()`, or `.update()` manually. Check out the
[`counters-stable` example](https://github.com/gbj/leptos/blob/main/examples/counters-stable/src/main.rs)
youll just call `.get()`, `.set()`, or `.update()` manually. Check out the
[`counters_stable` example](https://github.com/leptos-rs/leptos/blob/main/examples/counters_stable/src/main.rs)
for examples of the correct API.
## `cargo-leptos`
[`cargo-leptos`](https://github.com/akesson/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our [starter template](https://github.com/leptos-rs/start).
[`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our [starter template](https://github.com/leptos-rs/start).
```bash
cargo install cargo-leptos
@@ -108,7 +106,7 @@ Sure! Obviously the `view` macro is for generating DOM nodes but you can use the
- Use event listeners to update signals
- Create effects to update the UI
I've put together a [very simple GTK example](https://github.com/gbj/leptos/blob/main/examples/gtk/src/main.rs) so you can see what I mean.
I've put together a [very simple GTK example](https://github.com/leptos-rs/leptos/blob/main/examples/gtk/src/main.rs) so you can see what I mean.
### How is this different from Yew/Dioxus?
@@ -126,7 +124,6 @@ There are some practical differences that make a significant difference:
- **Maturity:** Sycamore is obviously a much more mature and stable library with a larger ecosystem.
- **Templating:** Leptos uses a JSX-like template format (built on [syn-rsx](https://github.com/stoically/syn-rsx)) for its `view` macro. Sycamore offers the choice of its own templating DSL or a builder syntax.
- **Template node cloning:** Leptos's `view` macro compiles to a static HTML string and a set of instructions of how to assign its reactive values. This means that at runtime, Leptos can clone a `<template>` node rather than calling `document.createElement()` to create DOM nodes. This is a _significantly_ faster way of rendering components.
- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use `create_rw_signal` to give a unified read/write signal.)_
- **Signals are functions:** In Leptos, you can call a signal to access it rather than calling a specific method (so, `count()` instead of `count.get()`) This creates a more consistent mental model: accessing a reactive value is always a matter of calling a function. For example:

View File

@@ -1,6 +1,6 @@
# Introduction
This book is intended as an introduction to the [Leptos](https://github.com/gbj/leptos) Web framework. Together, well build a simple todo app—first as a client-side app, then as a full-stack app.
This book is intended as an introduction to the [Leptos](https://github.com/leptos-rs/leptos) Web framework. Together, well build a simple todo app—first as a client-side app, then as a full-stack app.
The guide doesnt assume you know anything about fine-grained reactivity or the details of modern Web frameworks. It does assume you are familiar with the Rust programming language, HTML, CSS, and the DOM and other Web APIs.

View File

@@ -1,8 +1,8 @@
# Getting Started
> The code for this chapter can be found [here](https://github.com/gbj/leptos/tree/main/docs/book/project/ch02_getting_started).
> The code for this chapter can be found [here](https://github.com/leptos-rs/leptos/tree/main/docs/book/project/ch02_getting_started).
The easiest way to get started using Leptos is to use [Trunk](https://trunkrs.dev/), as many of our [examples](https://github.com/gbj/leptos/tree/main/examples) do. (Trunk is a simple build tool that includes a dev server.)
The easiest way to get started using Leptos is to use [Trunk](https://trunkrs.dev/), as many of our [examples](https://github.com/leptos-rs/leptos/tree/main/examples) do. (Trunk is a simple build tool that includes a dev server.)
If you dont already have it installed, you can install Trunk by running

View File

@@ -1,6 +1,6 @@
# Templating: Building User Interfaces
> The code for this chapter can be found [here](https://github.com/gbj/leptos/tree/main/docs/book/project/ch03_building_ui).
> The code for this chapter can be found [here](https://github.com/leptos-rs/leptos/tree/main/docs/book/project/ch03_building_ui).
## RSX and the `view!` macro

View File

@@ -25,7 +25,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
1. Install wasm-pack
```bash
cargo install wasm-pack

View File

@@ -33,7 +33,9 @@ cfg_if! {
crate::counters::register_server_functions();
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
let routes = generate_route_list(|cx| view! { cx, <Counters/> });

View File

@@ -25,7 +25,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes..
1. Install wasm-pack
```bash
cargo install wasm-pack

View File

@@ -21,7 +21,9 @@ cfg_if! {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|cx| view! { cx, <App/> });

View File

@@ -25,7 +25,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes..
1. Install wasm-pack
```bash
cargo install wasm-pack

View File

@@ -1,6 +1,6 @@
# Leptos Starter Template
This is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/gbj/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
This is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
If you don't have `cargo-leptos` installed you can install it with
@@ -52,13 +52,11 @@ If you're using VS Code, add the following to your `settings.json`
"css.validate": false,
```
Install [Tailwind CSS Intellisense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss).
Install "VS Browser" extension, a browser at the right window.
Allow vscode Ports forward: 3000, 3001.
## Notes about Tooling
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
@@ -72,8 +70,8 @@ By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If
## Alternatives to cargo-leptos
This crate can be run without `cargo-leptos`, using `wasm-pack` and `cargo`. To do so, you'll need to install some other tools.
1. `cargo install wasm-pack`
0. `cargo install wasm-pack`
1. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
### Server Side Rendering With Hydration
@@ -91,15 +89,16 @@ Then run the server with `cargo run` to serve the server side rendered HTML and
cargo run --no-default-features --features=ssr
```
> Note that if your hydration code changes, you will have to rerun the wasm-pack command above before running
> Note that if your hydration code changes, you will have to rerun the wasm-pack command above before running
> `cargo run`
### Client Side Rendering
You'll need to install trunk to client side render this bundle.
1. `cargo install trunk`
Then the site can be served with `trunk serve --open`
Then the site can be served with `trunk serve --open`
## Attribution
Many thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/gbj/leptos/discussions/125).
Many thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/leptos-rs/leptos/discussions/125).

View File

@@ -17,7 +17,9 @@ cfg_if! {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
// Generate the list of routes in your Leptos App

View File

@@ -25,7 +25,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS file is no longer processed by cargo-leptos. Building to alternative folders is not supported at this time
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
1. Install wasm-pack
```bash
cargo install wasm-pack

View File

@@ -26,7 +26,9 @@ cfg_if! {
crate::todo::register_server_functions();
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
// Generate the list of routes in your Leptos App

View File

@@ -26,7 +26,8 @@ if #[cfg(feature = "ssr")] {
crate::todo::register_server_functions();
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_address.clone();
let routes = generate_route_list(|cx| view! { cx, <TodoApp/> }).await;

View File

@@ -1,10 +1,10 @@
[package]
name = "leptos_actix"
version = {workspace = true}
version = { workspace = true }
edition = "2021"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/gbj/leptos"
repository = "https://github.com/leptos-rs/leptos"
description = "Actix integrations for the Leptos web framework."
[dependencies]

View File

@@ -259,20 +259,22 @@ where
let options = options.clone();
let app_fn = app_fn.clone();
let res_options = ResponseOptions::default();
let status = RouterStatusContext::default();
async move {
let app = {
let app_fn = app_fn.clone();
let res_options = res_options.clone();
let status = status.clone();
move |cx| {
provide_contexts(cx, &req, res_options);
provide_contexts(cx, &req, res_options, status);
(app_fn)(cx).into_view(cx)
}
};
let (head, tail) = html_parts(&options);
stream_app(app, head, tail, res_options).await
stream_app(app, head, tail, res_options, status).await
}
})
}
@@ -336,6 +338,7 @@ where
let app_fn = app_fn.clone();
let data_fn = data_fn.clone();
let res_options = ResponseOptions::default();
let status = RouterStatusContext::default();
async move {
let data = match data_fn(req.clone()).await {
@@ -347,24 +350,31 @@ where
let app = {
let app_fn = app_fn.clone();
let res_options = res_options.clone();
let status = status.clone();
move |cx| {
provide_contexts(cx, &req, res_options);
provide_contexts(cx, &req, res_options, status);
(app_fn)(cx, data).into_view(cx)
}
};
let (head, tail) = html_parts(&options);
stream_app(app, head, tail, res_options).await
stream_app(app, head, tail, res_options, status).await
}
})
}
fn provide_contexts(cx: leptos::Scope, req: &HttpRequest, res_options: ResponseOptions) {
fn provide_contexts(
cx: leptos::Scope,
req: &HttpRequest,
res_options: ResponseOptions,
status: RouterStatusContext,
) {
let path = leptos_corrected_path(req);
let integration = ServerIntegration { path };
provide_context(cx, RouterIntegrationContext::new(integration));
provide_context(cx, status);
provide_context(cx, MetaContext::new());
provide_context(cx, res_options);
provide_context(cx, req.clone());
@@ -385,6 +395,7 @@ async fn stream_app(
head: String,
tail: String,
res_options: ResponseOptions,
router_status: RouterStatusContext,
) -> HttpResponse<BoxBody> {
let (stream, runtime, _) = render_to_stream_with_prefix_undisposed(app, move |cx| {
let head = use_context::<MetaContext>(cx)
@@ -411,7 +422,15 @@ async fn stream_app(
let res_options = res_options.0.read().await;
let (status, mut headers) = (res_options.status, res_options.headers.clone());
let status = status.unwrap_or_default();
let status = status.unwrap_or_else(|| {
router_status
.status
.read()
.ok()
.and_then(|s| s.map(|s| StatusCode::from_u16(s).ok()))
.flatten()
.unwrap_or_default()
});
let complete_stream = futures::stream::iter([
first_chunk.unwrap(),

View File

@@ -1,10 +1,10 @@
[package]
name = "leptos_axum"
version = {workspace = true}
version = { workspace = true }
edition = "2021"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/gbj/leptos"
repository = "https://github.com/leptos-rs/leptos"
description = "Axum integrations for the Leptos web framework."
[dependencies]

View File

@@ -318,6 +318,8 @@ where
let default_res_options = ResponseOptions::default();
let res_options2 = default_res_options.clone();
let res_options3 = default_res_options.clone();
let router_status = RouterStatusContext::default();
let router_status2 = router_status.clone();
async move {
// Need to get the path and query string of the Request
@@ -403,6 +405,7 @@ where
cx,
RouterIntegrationContext::new(integration),
);
provide_context(cx, router_status2);
provide_context(cx, MetaContext::new());
provide_context(cx, req_parts);
provide_context(cx, default_res_options);
@@ -471,9 +474,16 @@ where
Box::pin(complete_stream) as PinnedHtmlStream
));
if let Some(status) = res_options.status {
*res.status_mut() = status
}
let status = res_options.status.unwrap_or_else(|| {
router_status
.status
.read()
.ok()
.and_then(|s| s.map(|s| StatusCode::from_u16(s).ok()))
.flatten()
.unwrap_or_default()
});
*res.status_mut() = status;
let mut res_headers = res_options.headers.clone();
res.headers_mut().extend(res_headers.drain());

View File

@@ -19,33 +19,33 @@
//! 1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.0", features = ["stable"] }`
//! 2. `nightly` enables the function call syntax for accessing and setting signals. If youre using `stable`,
//! youll just call `.get()`, `.set()`, or `.update()` manually. Check out the
//! [`counters_stable` example](https://github.com/gbj/leptos/blob/main/examples/counters_stable/src/main.rs)
//! [`counters_stable` example](https://github.com/leptos-rs/leptos/blob/main/examples/counters_stable/src/main.rs)
//! for examples of the correct API.
//!
//! # Learning by Example
//!
//! These docs are a work in progress. If you want to see what Leptos is capable of, check out
//! the [examples](https://github.com/gbj/leptos/tree/main/examples):
//! - [`counter`](https://github.com/gbj/leptos/tree/main/examples/counter) is the classic
//! the [examples](https://github.com/leptos-rs/leptos/tree/main/examples):
//! - [`counter`](https://github.com/leptos-rs/leptos/tree/main/examples/counter) is the classic
//! counter example, showing the basics of client-side rendering and reactive DOM updates
//! - [`counters`](https://github.com/gbj/leptos/tree/main/examples/counter) introduces parent-child
//! - [`counters`](https://github.com/leptos-rs/leptos/tree/main/examples/counters) introduces parent-child
//! communication via contexts, and the `<For/>` component for efficient keyed list updates.
//! - [`parent_child`](https://github.com/gbj/leptos/tree/main/examples/parent_child) shows four different
//! - [`parent_child`](https://github.com/leptos-rs/leptos/tree/main/examples/parent_child) shows four different
//! ways a parent component can communicate with a child, including passing a closure, context, and more
//! - [`todomvc`](https://github.com/gbj/leptos/tree/main/examples/todomvc) implements the classic to-do
//! - [`todomvc`](https://github.com/leptos-rs/leptos/tree/main/examples/todomvc) implements the classic to-do
//! app in Leptos. This is a good example of a complete, simple app. In particular, you might want to
//! see how we use [create_effect] to [serialize JSON to `localStorage`](https://github.com/gbj/leptos/blob/16f084a71268ac325fbc4a5e50c260df185eadb6/examples/todomvc/src/lib.rs#L164)
//! and [reactively call DOM methods](https://github.com/gbj/leptos/blob/6d7c36655c9e7dcc3a3ad33d2b846a3f00e4ae74/examples/todomvc/src/lib.rs#L291)
//! on [references to elements](https://github.com/gbj/leptos/blob/6d7c36655c9e7dcc3a3ad33d2b846a3f00e4ae74/examples/todomvc/src/lib.rs#L254).
//! - [`fetch`](https://github.com/gbj/leptos/tree/main/examples/fetch) introduces
//! see how we use [create_effect] to [serialize JSON to `localStorage`](https://github.com/leptos-rs/leptos/blob/16f084a71268ac325fbc4a5e50c260df185eadb6/examples/todomvc/src/lib.rs#L164)
//! and [reactively call DOM methods](https://github.com/leptos-rs/leptos/blob/6d7c36655c9e7dcc3a3ad33d2b846a3f00e4ae74/examples/todomvc/src/lib.rs#L291)
//! on [references to elements](https://github.com/leptos-rs/leptos/blob/6d7c36655c9e7dcc3a3ad33d2b846a3f00e4ae74/examples/todomvc/src/lib.rs#L254).
//! - [`fetch`](https://github.com/leptos-rs/leptos/tree/main/examples/fetch) introduces
//! [Resource](leptos_reactive::Resource)s, which allow you to integrate arbitrary `async` code like an
//! HTTP request within your reactive code.
//! - [`router`](https://github.com/gbj/leptos/tree/main/examples/router) shows how to use Leptoss nested router
//! - [`router`](https://github.com/leptos-rs/leptos/tree/main/examples/router) shows how to use Leptoss nested router
//! to enable client-side navigation and route-specific, reactive data loading.
//! - [`todomvc`](https://github.com/gbj/leptos/tree/main/examples/todomvc) shows the basics of building an
//! - [`todomvc`](https://github.com/leptos-rs/leptos/tree/main/examples/todomvc) shows the basics of building an
//! isomorphic web app. Both the server and the client import the same app code from the `todomvc` example.
//! The server renders the app directly to an HTML string, and the client hydrates that HTML to make it interactive.
//! - [`hackernews`](https://github.com/gbj/leptos/tree/main/examples/hackernews) pulls everything together.
//! - [`hackernews`](https://github.com/leptos-rs/leptos/tree/main/examples/hackernews) pulls everything together.
//! It integrates calls to a real external REST API, routing, server-side rendering and hydration to create
//! a fully-functional PEMPA that works as intended even before WASM has loaded and begun to run.
//!

View File

@@ -1,7 +1,7 @@
use cfg_if::cfg_if;
use leptos_dom::{Component, DynChild, Fragment, IntoView};
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
use leptos_dom::{HydrationCtx, HydrationKey};
use leptos_dom::HydrationCtx;
use leptos_dom::{DynChild, Fragment, IntoView};
use leptos_macro::component;
use leptos_reactive::{provide_context, Scope, SuspenseContext};
use std::rc::Rc;
@@ -73,7 +73,7 @@ where
let orig_child = Rc::new(children);
Component::new("Suspense", move |cx| {
leptos_dom::custom(cx, leptos_dom::Custom::new("leptos-suspense")).child({
#[cfg(not(any(feature = "csr", feature = "hydrate")))]
let current_id = HydrationCtx::peek();
@@ -104,13 +104,9 @@ where
&current_id.to_string(),
{
let current_id = current_id.clone();
let fragment_id = HydrationKey {
previous: current_id.previous,
offset: current_id.offset + 1
};
move || {
HydrationCtx::continue_from(fragment_id);
orig_child(cx)
HydrationCtx::continue_from(current_id.clone());
DynChild::new(move || orig_child(cx))
.into_view(cx)
.render_to_string(cx)
.to_string()

View File

@@ -152,7 +152,10 @@ impl TryFrom<String> for Env {
}
/// Loads [LeptosOptions] from a Cargo.toml with layered overrides. If an env var is specified, like `LEPTOS_ENV`,
/// it will override a setting in the file.
/// it will override a setting in the file. It takes in an optional path to a Cargo.toml file. If None is provided,
/// you'll need to set the options as environment variables or rely on the defaults. This is the preferred
/// approach for cargo-leptos. If Some("./Cargo.toml") is provided, Leptos will read in the settings itself. This
/// option currently does not allow dashes in file or foldernames, as all dashes become underscores
pub async fn get_configuration(path: Option<&str>) -> Result<ConfFile, LeptosConfigError> {
if let Some(path) = path {
let text = fs::read_to_string(path).map_err(|_| LeptosConfigError::ConfigNotFound)?;

View File

@@ -4,7 +4,7 @@ version = { workspace = true }
edition = "2021"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/gbj/leptos"
repository = "https://github.com/leptos-rs/leptos"
description = "DOM operations for the Leptos web framework."
[dependencies]

View File

@@ -165,6 +165,66 @@ pub struct Custom {
id: HydrationKey,
}
impl Custom {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
let name = name.into();
let id = HydrationCtx::id();
#[cfg(all(target_arch = "wasm32", feature = "web"))]
let element = if HydrationCtx::is_hydrating() {
if let Some(el) = crate::document().get_element_by_id(&format!("_{id}")) {
#[cfg(debug_assertions)]
assert_eq!(
el.node_name().to_ascii_uppercase(),
name.to_ascii_uppercase(),
"SSR and CSR elements have the same `TopoId` but different node \
kinds. This is either a discrepancy between SSR and CSR rendering
logic, which is considered a bug, or it can also be a \
leptos hydration issue."
);
el.remove_attribute("id").unwrap();
el.unchecked_into()
} else if let Ok(Some(el)) =
crate::document().query_selector(&format!("[leptos-hk=_{id}]"))
{
#[cfg(debug_assertions)]
assert_eq!(
el.node_name().to_ascii_uppercase(),
name.to_ascii_uppercase(),
"SSR and CSR elements have the same `TopoId` but different node \
kinds. This is either a discrepancy between SSR and CSR rendering
logic, which is considered a bug, or it can also be a \
leptos hydration issue."
);
el.remove_attribute("leptos-hk").unwrap();
el.unchecked_into()
} else {
gloo::console::warn!(
"element with id",
format!("_{id}"),
"not found, ignoring it for hydration"
);
crate::document().create_element(&name).unwrap()
}
} else {
crate::document().create_element(&name).unwrap()
};
Self {
name,
#[cfg(all(target_arch = "wasm32", feature = "web"))]
element: element.unchecked_into(),
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
id,
}
}
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
impl std::ops::Deref for Custom {
type Target = web_sys::HtmlElement;

View File

@@ -149,45 +149,19 @@ pub fn render_to_stream_with_prefix_undisposed(
// resources and fragments
// stream HTML for each <Suspense/> as it resolves
let fragments = fragments.map(|(fragment_id, id_before_suspense, html)| {
cfg_if! {
if #[cfg(debug_assertions)] {
_ = id_before_suspense;
// Debug-mode <Suspense/>-replacement code
format!(
r#"
<template id="{fragment_id}f">{html}</template>
<script>
var start = document.getElementById("_{fragment_id}o");
var end = document.getElementById("_{fragment_id}c");
var range = new Range();
range.setStartBefore(start.nextSibling.nextSibling);
range.setEndAfter(end.previousSibling.previousSibling);
range.deleteContents();
var tpl = document.getElementById("{fragment_id}f");
end.parentNode.insertBefore(tpl.content.cloneNode(true), end.previousSibling);
</script>
"#
)
} else {
// Release-mode <Suspense/>-replacement code
format!(
r#"
<template id="{fragment_id}f">{html}</template>
<script>
var start = document.getElementById("_{id_before_suspense}");
var end = document.getElementById("_{fragment_id}");
var range = new Range();
range.setStartAfter(start);
range.setEndBefore(end);
range.deleteContents();
var tpl = document.getElementById("{fragment_id}f");
end.parentNode.insertBefore(tpl.content.cloneNode(true), end.previousSibling);
</script>
"#
)
}
}
// TODO can remove id_before_suspense entirely now
let fragments = fragments.map(|(fragment_id, _, html)| {
format!(
r#"
<template id="{fragment_id}f">{html}</template>
<script>
var placeholder = document.getElementById("_{fragment_id}");
var tpl = document.getElementById("{fragment_id}f");
placeholder.textContent = "";
placeholder.append(tpl.content.cloneNode(true));
</script>
"#
)
});
// stream data for each Resource as it resolves
let resources = serializers.map(|(id, json)| {
@@ -425,6 +399,7 @@ impl View {
}
}
#[cfg(debug_assertions)]
fn to_kebab_case(name: &str) -> String {
if name.is_empty() {
return String::new();

View File

@@ -4,7 +4,7 @@ version = { workspace = true }
edition = "2021"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/gbj/leptos"
repository = "https://github.com/leptos-rs/leptos"
description = "view macro for the Leptos web framework."
[lib]

View File

@@ -49,7 +49,7 @@ pub fn server_macro_impl(args: proc_macro::TokenStream, s: TokenStream2) -> Resu
#[cfg(not(target_os = "windows"))]
let url = format!("{}/{}", span.source_file().path().to_string_lossy(), fn_name_as_str).replace('/', "-");
#[cfg(target_os = "windows")]
let url = format!("{}/{}", span.source_file().path().to_string_lossy(), fn_name_as_str).replace("\\", "-");
let url = format!("{}\\{}", span.source_file().path().to_string_lossy(), fn_name_as_str).replace("\\", "-");
} else {
let url = fn_name_as_str;
}

View File

@@ -554,7 +554,7 @@ fn element_to_tokens(cx: &Ident, node: &NodeElement, mut parent_type: TagType) -
let tag = node.name.to_string();
let name = if is_custom_element(&tag) {
let name = node.name.to_string();
quote! { leptos::leptos_dom::custom(#cx, #name) }
quote! { leptos::leptos_dom::custom(#cx, leptos::leptos_dom::Custom::new(#name)) }
} else if is_svg_element(&tag) {
let name = &node.name;
parent_type = TagType::Svg;

View File

@@ -1,21 +1,20 @@
use crate::{create_memo, IntoSignalSetter, RwSignal, Scope, Signal, SignalSetter};
/// derives a reactive slice from an [RwSignal](crate::RwSignal)
/// Derives a reactive slice of an [RwSignal](crate::RwSignal).
///
/// Slices have the same guarantees as [Memos](crate::Memo),
/// Slices have the same guarantees as [Memos](crate::Memo):
/// they only emit their value when it has actually been changed.
///
/// slices need a getter and a setter, and you must make sure that
/// Slices need a getter and a setter, and you must make sure that
/// the setter and getter only touch their respective field and nothing else.
/// They optimally should not have any side effects.
///
/// you can use slices whenever you want to react to only parts
/// of a bigger signal, the prime example would be state management
/// where you want all state variables grouped up but also need
/// You can use slices whenever you want to react to only parts
/// of a bigger signal. The prime example would be state management,
/// where you want all state variables grouped together, but also need
/// fine-grained signals for each or some of these variables.
/// In the example below, setting an auth token will only trigger
/// the token signal, but none of the other derived signals.
///
/// ```
/// # use leptos_reactive::*;
/// # let (cx, disposer) = raw_scope_and_disposer(create_runtime());
@@ -49,13 +48,14 @@ use crate::{create_memo, IntoSignalSetter, RwSignal, Scope, Signal, SignalSetter
/// );
/// let count_token_updates = create_rw_signal(cx, 0);
/// count_token_updates.with(|counter| assert_eq!(counter, &0));
/// create_effect(cx, move |_| {
/// token.with(|_| {});
/// create_isomorphic_effect(cx, move |_| {
/// _ = token.with(|_| {});
/// count_token_updates.update(|counter| *counter += 1)
/// });
/// count_token_updates.with(|counter| assert_eq!(counter, &1));
/// set_token.set("this is not a token!".into());
/// // token was updated with the new token
/// token.with(|token| assert_eq!(token, "this is not a token!"));
/// count_token_updates.with(|counter| assert_eq!(counter, &2));
/// set_dark_mode.set(true);
/// // since token didn't change, there was also no update emitted

View File

@@ -4,7 +4,7 @@
//! # Leptos Meta
//!
//! Leptos Meta allows you to modify content in a documents `<head>` from within components
//! using the [Leptos](https://github.com/gbj/leptos) web framework.
//! using the [Leptos](https://github.com/leptos-rs/leptos) web framework.
//!
//! Document metadata is updated automatically when running in the browser. For server-side
//! rendering, after the component tree is rendered to HTML, [MetaContext::dehydrate] can generate

View File

@@ -17,7 +17,7 @@ use crate::{use_head, TextProp};
/// <main>
/// <Meta charset="utf-8"/>
/// <Meta name="description" content="A Leptos fan site."/>
/// <Meta http_equiv="refresh" content="3;url=https://github.com/gbj/leptos"/>
/// <Meta http_equiv="refresh" content="3;url=https://github.com/leptos-rs/leptos"/>
/// </main>
/// }
/// }

View File

@@ -138,7 +138,7 @@ impl RouteContext {
self.inner.params
}
pub(crate) fn base(cx: Scope, path: &str, fallback: Option<fn() -> View>) -> Self {
pub(crate) fn base(cx: Scope, path: &str, fallback: Option<fn(Scope) -> View>) -> Self {
Self {
inner: Rc::new(RouteContextInner {
cx,
@@ -148,7 +148,7 @@ impl RouteContext {
path: path.to_string(),
original_path: path.to_string(),
params: create_memo(cx, |_| ParamsMap::new()),
outlet: Box::new(move || fallback.map(|f| f().into_view(cx))),
outlet: Box::new(move || fallback.as_ref().map(move |f| f(cx))),
}),
}
}

View File

@@ -1,5 +1,9 @@
use cfg_if::cfg_if;
use std::{cell::RefCell, rc::Rc};
use std::{
cell::RefCell,
rc::Rc,
sync::{Arc, RwLock},
};
use leptos::*;
use thiserror::Error;
@@ -28,7 +32,7 @@ pub fn Router(
base: Option<&'static str>,
/// A fallback that should be shown if no route is matched.
#[prop(optional)]
fallback: Option<fn() -> View>,
fallback: Option<fn(Scope) -> View>,
/// The `<Router/>` should usually wrap your whole page. It can contain
/// any elements, and should include a [Routes](crate::Routes) component somewhere
/// to define and display [Route](crate::Route)s.
@@ -61,6 +65,14 @@ pub(crate) struct RouterContextInner {
set_state: WriteSignal<State>,
}
/// Context type that indicates the status of the last request
/// (i.e., whether it was not found, or had an error.)
#[derive(Debug, Clone, Default)]
pub struct RouterStatusContext {
pub status: Arc<RwLock<Option<u16>>>,
pub message: Arc<RwLock<Option<String>>>,
}
impl std::fmt::Debug for RouterContextInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RouterContextInner")
@@ -80,7 +92,7 @@ impl RouterContext {
pub(crate) fn new(
cx: Scope,
base: Option<&'static str>,
fallback: Option<fn() -> View>,
fallback: Option<fn(Scope) -> View>,
) -> Self {
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {

View File

@@ -12,7 +12,7 @@ use crate::{
expand_optionals, get_route_matches, join_paths, Branch, Matcher, RouteDefinition,
RouteMatch,
},
RouteContext, RouterContext,
RouteContext, RouterContext, RouterStatusContext,
};
/// Contains route definitions and manages the actual routing process.
@@ -28,6 +28,7 @@ pub fn Routes(
log::warn!("<Routes/> component should be nested within a <Router/>.");
panic!()
});
let base_route = router.base();
let mut branches = Vec::new();
let id_before = HydrationCtx::peek();
@@ -189,19 +190,35 @@ pub fn Routes(
});
// show the root route
let router_status = use_context::<RouterStatusContext>(cx);
let root = create_memo(cx, move |prev| {
provide_context(cx, route_states);
let router_status = router_status.clone();
route_states.with(|state| {
let root = state.routes.borrow();
let root = root.get(0);
if let Some(route) = root {
provide_context(cx, route.clone());
}
if prev.is_none() || !root_equal.get() {
root.as_ref().map(|route| route.outlet().into_view(cx))
if state.routes.borrow().is_empty() {
if let Some(status) = router_status {
if let Ok(mut lock) = status.status.write() {
*lock = Some(404);
}
}
Some(base_route.outlet().into_view(cx))
} else {
prev.cloned().unwrap()
if let Some(status) = router_status {
if let Ok(mut lock) = status.status.write() {
*lock = None;
}
}
let root = state.routes.borrow();
let root = root.get(0);
if let Some(route) = root {
provide_context(cx, route.clone());
}
if prev.is_none() || !root_equal.get() {
root.as_ref().map(|route| route.outlet().into_view(cx))
} else {
prev.cloned().unwrap()
}
}
})
});