autumn_plugin_leptos/lib.rs
1//! Leptos islands for Autumn apps: Leptos 0.8 components (wasm) inside
2//! Maud + htmx pages.
3//!
4//! 1. Write the components in a wasm crate. Register them with
5//! `autumn-plugin-leptos-client` in the start function:
6//!
7//! ```rust,ignore
8//! use autumn_plugin_leptos_client::register;
9//! use leptos::prelude::*;
10//! use wasm_bindgen::prelude::*;
11//!
12//! #[wasm_bindgen(start)]
13//! pub fn start() {
14//! register("Counter", counter);
15//! }
16//! ```
17//!
18//! 2. Build it with `cargo build --target wasm32-unknown-unknown` and
19//! `wasm-bindgen --target web`. Embed the output as a `PluginAssets`
20//! bundle and install it:
21//!
22//! ```rust,no_run
23//! use autumn_plugin_leptos::{Island, LeptosPlugin, leptos_bundle, leptos_script};
24//! use autumn_web::assets::PluginAssets;
25//! use autumn_web::prelude::*;
26//!
27//! static ISLANDS: PluginAssets = PluginAssets::from_files(
28//! "app-islands",
29//! &[("app.js", b"/* glue */"), ("app_bg.wasm", b"\0asm\x01\0\0\0")],
30//! );
31//!
32//! #[get("/")]
33//! async fn index() -> AutumnResult<Markup> {
34//! let counter = Island::new("Counter")
35//! .props(&serde_json::json!({ "start": 3 }))?
36//! .fallback(html! { p { "Count: 3" } });
37//! Ok(html! {
38//! html {
39//! head { (leptos_script()) (leptos_bundle(&ISLANDS)) }
40//! body { (counter) }
41//! }
42//! })
43//! }
44//!
45//! # async fn run() {
46//! autumn_web::app()
47//! .plugin(LeptosPlugin::new().bundle(&ISLANDS))
48//! .routes(routes![index])
49//! .run()
50//! .await;
51//! # }
52//! ```
53//!
54//! 3. Set a CSP that allows wasm: [`WasmCspLoader`] or [`wasm_csp()`].
55//!
56//! The loader (`leptos-islands.js`) imports each bundle and mounts each
57//! island. It mounts islands that htmx swaps in and unmounts islands that
58//! htmx swaps out. A `data-leptos-props` change sets the props signal of
59//! the same instance, so the component keeps its state. It uses no inline
60//! script and no `eval`.
61//!
62//! # Features
63//!
64//! - `ssr`: [`Island::fallback_view`] renders a Leptos view on the server
65//! as the fallback.
66//!
67//! # Security
68//!
69//! The loader mounts each `data-leptos-island` element in the page and
70//! imports each accepted bundle link. If your app shows user HTML, the
71//! sanitizer must remove `data-leptos-*`, `hx-*` and `data-hx-*`
72//! attributes and `<link>` and `<template>` elements. `data-leptos-ignore`
73//! does not give full protection. An htmx out-of-band swap can move an
74//! element out of it.
75
76mod assets;
77mod csp;
78mod island;
79mod plugin;
80mod tags;
81mod update;
82
83pub use assets::{ASSETS_NAMESPACE, LEPTOS_ASSETS, LOADER_JS};
84pub use csp::{WASM_UNSAFE_EVAL, WasmCspLoader, add_wasm_unsafe_eval, wasm_csp};
85pub use island::{AttrError, Island, JsonKind, MountWhen, PropsError};
86pub use plugin::{LeptosPlugin, PLUGIN_NAME};
87pub use tags::{leptos_bundle, leptos_script};
88pub use update::{PROPS_EVENT, PropsUpdate};