diff --git a/examples/hackernews_js_fetch/Cargo.toml b/examples/hackernews_js_fetch/Cargo.toml index 11c1e14ca..d08d22474 100644 --- a/examples/hackernews_js_fetch/Cargo.toml +++ b/examples/hackernews_js_fetch/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "hackernews-js-fetch" +name = "hackernews_js_fetch" version = "0.1.0" edition = "2021" @@ -11,21 +11,20 @@ codegen-units = 1 lto = true [dependencies] -console_log = "1.0.0" -console_error_panic_hook = "0.1.7" -cfg-if = "1.0.0" +console_error_panic_hook = "0.1" +console_log = "1.0" +log = "0.4" leptos = { path = "../../leptos" } leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true } leptos_meta = { path = "../../meta" } leptos_router = { path = "../../router" } -log = "0.4.17" -simple_logger = "4.0.0" -serde = { version = "1.0.148", features = ["derive"] } +leptos_server = { path = "../../leptos_server", optional = true } +serde = { version = "1.0", features = ["derive"] } tracing = "0.1" gloo-net = { version = "0.6", features = ["http"] } reqwest = { version = "0.12", features = ["json"] } axum = { version = "0.7", default-features = false, optional = true } -tower = { version = "0.4.13", optional = true } +tower = { version = "0.4", optional = true } http = { version = "1.0", optional = true } web-sys = { version = "0.3", features = [ "AbortController", @@ -33,41 +32,43 @@ web-sys = { version = "0.3", features = [ "Request", "Response", ] } +getrandom = { version = "0.2.7", features = ["js"] } wasm-bindgen = "0.2" wasm-bindgen-futures = { version = "0.4.37", features = [ "futures-core-03-stream", ], optional = true } axum-js-fetch = { git = "https://github.com/seanaye/axum-js-fetch", optional = true } -lazy_static = "1.4.0" +send_wrapper = { version = "0.6.0", features = ["futures"] } [features] -hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] +hydrate = ["leptos/hydrate"] ssr = [ + "dep:axum", "dep:tower", "dep:http", - "dep:axum", "dep:wasm-bindgen-futures", "dep:axum-js-fetch", "leptos/ssr", "leptos_axum/wasm", "leptos_meta/ssr", "leptos_router/ssr", + "leptos_server/serde-wasm-bindgen", ] [package.metadata.cargo-all-features] denylist = ["axum", "tower", "http", "leptos_axum"] -skip_feature_sets = [["ssr", "hydrate"]] +skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]] [package.metadata.leptos] # The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name -output-name = "hackernews_axum" +output-name = "hackernews_js_fetch" # The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup. site-root = "target/site" # The site-root relative folder where all compiled output (JS, WASM and CSS) is written # Defaults to pkg site-pkg-dir = "pkg" # [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to //app.css -style-file = "./style.css" +style-file = "style.css" # [Optional] Files in the asset-dir will be copied to the site-root directory assets-dir = "public" # The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup. diff --git a/examples/hackernews_js_fetch/Makefile.toml b/examples/hackernews_js_fetch/Makefile.toml index 6d2dcf0bf..0a7519bc2 100644 --- a/examples/hackernews_js_fetch/Makefile.toml +++ b/examples/hackernews_js_fetch/Makefile.toml @@ -1,6 +1,6 @@ extend = [ - { path = "../cargo-make/main.toml" }, - { path = "../cargo-make/deno-build.toml" }, + { path = "../cargo-make/main.toml" }, + { path = "../cargo-make/deno-build.toml" }, ] [env] diff --git a/examples/hackernews_js_fetch/README.md b/examples/hackernews_js_fetch/README.md index c23a03029..e1f22c225 100644 --- a/examples/hackernews_js_fetch/README.md +++ b/examples/hackernews_js_fetch/README.md @@ -2,8 +2,6 @@ This example uses the basic Hacker News example as its basis, but shows how to run the server side as WASM running in a JS environment. In this example, Deno is used as the runtime. -**NOTE**: This example is slightly out of date pending an update to [`axum-js-fetch`](https://github.com/seanaye/axum-js-fetch/), which was waiting on a version of `gloo-net` that uses `http` 1.0. It still works with Leptos 0.5 and Axum 0.6, but not with the versions of Leptos (0.6 and later) that support Axum 1.0. - ## Server Side Rendering with Deno To run the Deno version, run diff --git a/examples/hackernews_js_fetch/deno.lock b/examples/hackernews_js_fetch/deno.lock index 7d8564bb9..81642bd5d 100644 --- a/examples/hackernews_js_fetch/deno.lock +++ b/examples/hackernews_js_fetch/deno.lock @@ -1,5 +1,8 @@ { - "version": "2", + "version": "3", + "redirects": { + "https://deno.land/std/http/file_server.ts": "https://deno.land/std@0.224.0/http/file_server.ts" + }, "remote": { "https://deno.land/std@0.177.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", "https://deno.land/std@0.177.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", @@ -53,6 +56,49 @@ "https://deno.land/std@0.177.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", "https://deno.land/std@0.177.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", "https://deno.land/std@0.177.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", - "https://deno.land/std@0.177.0/version.ts": "259c8866ec257c3511b437baa95205a86761abaef852a9b2199072accb2ef046" + "https://deno.land/std@0.177.0/version.ts": "259c8866ec257c3511b437baa95205a86761abaef852a9b2199072accb2ef046", + "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", + "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", + "https://deno.land/std@0.224.0/cli/parse_args.ts": "5250832fb7c544d9111e8a41ad272c016f5a53f975ef84d5a9fe5fcb70566ece", + "https://deno.land/std@0.224.0/encoding/_util.ts": "beacef316c1255da9bc8e95afb1fa56ed69baef919c88dc06ae6cb7a6103d376", + "https://deno.land/std@0.224.0/encoding/base64.ts": "dd59695391584c8ffc5a296ba82bcdba6dd8a84d41a6a539fbee8e5075286eaf", + "https://deno.land/std@0.224.0/fmt/bytes.ts": "7b294a4b9cf0297efa55acb55d50610f3e116a0ac772d1df0ae00f0b833ccd4a", + "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", + "https://deno.land/std@0.224.0/http/etag.ts": "9ca56531be682f202e4239971931060b688ee5c362688e239eeaca39db9e72cb", + "https://deno.land/std@0.224.0/http/file_server.ts": "2a5392195b8e7713288f274d071711b705bb5b3220294d76cce495d456c61a93", + "https://deno.land/std@0.224.0/http/status.ts": "ed61b4882af2514a81aefd3245e8df4c47b9a8e54929a903577643d2d1ebf514", + "https://deno.land/std@0.224.0/media_types/_db.ts": "19563a2491cd81b53b9c1c6ffd1a9145c355042d4a854c52f6e1424f73ff3923", + "https://deno.land/std@0.224.0/media_types/_util.ts": "e0b8da0c7d8ad2015cf27ac16ddf0809ac984b2f3ec79f7fa4206659d4f10deb", + "https://deno.land/std@0.224.0/media_types/content_type.ts": "ed3f2e1f243b418ad3f441edc95fd92efbadb0f9bde36219c7564c67f9639513", + "https://deno.land/std@0.224.0/media_types/format_media_type.ts": "ffef4718afa2489530cb94021bb865a466eb02037609f7e82899c017959d288a", + "https://deno.land/std@0.224.0/media_types/get_charset.ts": "277ebfceb205bd34e616fe6764ef03fb277b77f040706272bea8680806ae3f11", + "https://deno.land/std@0.224.0/media_types/parse_media_type.ts": "487f000a38c230ccbac25420a50f600862e06796d0eee19d19631b9e84ee9654", + "https://deno.land/std@0.224.0/media_types/type_by_extension.ts": "bf4e3f5d6b58b624d5daa01cbb8b1e86d9939940a77e7c26e796a075b60ec73b", + "https://deno.land/std@0.224.0/media_types/vendor/mime-db.v1.52.0.ts": "0218d2c7d900e8cd6fa4a866e0c387712af4af9a1bae55d6b2546c73d273a1e6", + "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8", + "https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c", + "https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3", + "https://deno.land/std@0.224.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607", + "https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15", + "https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36", + "https://deno.land/std@0.224.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441", + "https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a", + "https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d", + "https://deno.land/std@0.224.0/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2", + "https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63", + "https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91", + "https://deno.land/std@0.224.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c", + "https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf", + "https://deno.land/std@0.224.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add", + "https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d", + "https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808", + "https://deno.land/std@0.224.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef", + "https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf", + "https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780", + "https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7", + "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972", + "https://deno.land/std@0.224.0/streams/byte_slice_stream.ts": "5bbdcadb118390affa9b3d0a0f73ef8e83754f59bb89df349add669dd9369713", + "https://deno.land/std@0.224.0/version.ts": "f6a28c9704d82d1c095988777e30e6172eb674a6570974a0d27a653be769bbbe" } } diff --git a/examples/hackernews_js_fetch/src/api.rs b/examples/hackernews_js_fetch/src/api.rs index 99c44c6e3..4ca650481 100644 --- a/examples/hackernews_js_fetch/src/api.rs +++ b/examples/hackernews_js_fetch/src/api.rs @@ -1,5 +1,5 @@ -use leptos::Serializable; -use serde::{Deserialize, Serialize}; +use leptos::logging; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; pub fn story(path: &str) -> String { format!("https://node-hnapi.herokuapp.com/{path}") @@ -10,46 +10,51 @@ pub fn user(path: &str) -> String { } #[cfg(not(feature = "ssr"))] -pub async fn fetch_api(path: &str) -> Option +pub fn fetch_api( + path: &str, +) -> impl std::future::Future> + Send + '_ where - T: Serializable, + T: Serialize + DeserializeOwned, { - let abort_controller = web_sys::AbortController::new().ok(); - let abort_signal = abort_controller.as_ref().map(|a| a.signal()); + use leptos::prelude::on_cleanup; + use send_wrapper::SendWrapper; - // abort in-flight requests if, e.g., we've navigated away from this page - leptos::on_cleanup(move || { - if let Some(abort_controller) = abort_controller { - abort_controller.abort() - } - }); + SendWrapper::new(async move { + let abort_controller = + SendWrapper::new(web_sys::AbortController::new().ok()); + let abort_signal = abort_controller.as_ref().map(|a| a.signal()); - let json = gloo_net::http::Request::get(path) - .abort_signal(abort_signal.as_ref()) - .send() - .await - .map_err(|e| log::error!("{e}")) - .ok()? - .text() - .await - .ok()?; + // abort in-flight requests if, e.g., we've navigated away from this page + on_cleanup(move || { + if let Some(abort_controller) = abort_controller.take() { + abort_controller.abort() + } + }); - T::de(&json).ok() + gloo_net::http::Request::get(path) + .abort_signal(abort_signal.as_ref()) + .send() + .await + .map_err(|e| logging::error!("{e}")) + .ok()? + .json() + .await + .ok() + }) } #[cfg(feature = "ssr")] pub async fn fetch_api(path: &str) -> Option where - T: Serializable, + T: Serialize + DeserializeOwned, { - let json = reqwest::get(path) + reqwest::get(path) .await - .map_err(|e| log::error!("{e}")) + .map_err(|e| logging::error!("{e}")) .ok()? - .text() + .json() .await - .ok()?; - T::de(&json).map_err(|e| log::error!("{e}")).ok() + .ok() } #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] diff --git a/examples/hackernews_js_fetch/src/error_template.rs b/examples/hackernews_js_fetch/src/error_template.rs deleted file mode 100644 index fa4dd7133..000000000 --- a/examples/hackernews_js_fetch/src/error_template.rs +++ /dev/null @@ -1,28 +0,0 @@ -use leptos::{view, Errors, For, IntoView, RwSignal, SignalGet, View}; - -// A basic function to display errors served by the error boundaries. Feel free to do more complicated things -// here than just displaying them -pub fn error_template(errors: Option>) -> View { - let Some(errors) = errors else { - panic!("No Errors found and we expected errors!"); - }; - - view! { -

"Errors"

- "Error: " {error_string}

- } - } - /> - } - .into_view() -} diff --git a/examples/hackernews_js_fetch/src/fallback.rs b/examples/hackernews_js_fetch/src/fallback.rs deleted file mode 100644 index dddebd1ca..000000000 --- a/examples/hackernews_js_fetch/src/fallback.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::error_template::error_template; -use axum::{ - body::Body, - extract::State, - http::{Request, Response, StatusCode, Uri}, - response::{IntoResponse, Response as AxumResponse}, -}; -//use tower::ServiceExt; -use leptos::LeptosOptions; - -pub async fn file_and_error_handler( - uri: Uri, - State(options): State, - req: Request, -) -> AxumResponse { - let root = options.site_root.clone(); - let res = get_static_file(uri.clone(), &root).await.unwrap(); - - if res.status() == StatusCode::OK { - res.into_response() - } else { - let handler = - leptos_axum::render_app_to_stream(options.to_owned(), || { - error_template(None) - }); - handler(req).await.into_response() - } -} - -async fn get_static_file( - uri: Uri, - root: &str, -) -> Result, (StatusCode, String)> { - let req = Request::builder() - .uri(uri.clone()) - .body(Body::empty()) - .unwrap(); - // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` - // This path is relative to the cargo root - _ = req; - _ = root; - todo!() -} diff --git a/examples/hackernews_js_fetch/src/lib.rs b/examples/hackernews_js_fetch/src/lib.rs index ec85418a6..c365687e7 100644 --- a/examples/hackernews_js_fetch/src/lib.rs +++ b/examples/hackernews_js_fetch/src/lib.rs @@ -1,46 +1,71 @@ -use leptos::{component, view, IntoView}; -use leptos_meta::*; -use leptos_router::*; +use leptos::prelude::*; mod api; -pub mod error_template; -#[cfg(feature = "ssr")] -pub mod fallback; mod routes; +use leptos_meta::{provide_meta_context, Link, Meta, MetaTags, Stylesheet}; +use leptos_router::{ + components::{FlatRoutes, Route, Router, RoutingProgress}, + ParamSegment, StaticSegment, +}; use routes::{nav::*, stories::*, story::*, users::*}; -use wasm_bindgen::prelude::wasm_bindgen; +use std::time::Duration; + +pub fn shell(options: LeptosOptions) -> impl IntoView { + view! { + + + + + + + + + + + + + + } +} #[component] pub fn App() -> impl IntoView { provide_meta_context(); + let (is_routing, set_is_routing) = signal(false); + view! { - + - -