aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2024-03-25 23:34:06 +0000
committerHayleigh Thompson <me@hayleigh.dev>2024-03-25 23:34:06 +0000
commitd06db4c3fdc01b88b14c0f47ab9a32684f874fbd (patch)
treef021477990b5cd830009ad636b6ecf23d4628512
parent01e81600d42d5510199ded74fd4f2c5bfb1aa4ab (diff)
downloadlustre-d06db4c3fdc01b88b14c0f47ab9a32684f874fbd.tar.gz
lustre-d06db4c3fdc01b88b14c0f47ab9a32684f874fbd.zip
:memo: Write some basic documentation on what server components are all about.
-rw-r--r--src/lustre/server_component.gleam112
1 files changed, 96 insertions, 16 deletions
diff --git a/src/lustre/server_component.gleam b/src/lustre/server_component.gleam
index e39e984..33123e4 100644
--- a/src/lustre/server_component.gleam
+++ b/src/lustre/server_component.gleam
@@ -3,6 +3,73 @@
//// > [open an issue](https://github.com/lustre-labs/lustre/issues/new) and tell
//// > us why it's important to you!
////
+//// Server components are an advanced feature that allows you to run entire
+//// Lustre applications on the server. DOM changes are broadcasted to a small
+//// client runtime and browser events are sent back to the server.
+////
+//// ```text
+//// -- SERVER -----------------------------------------------------------------
+////
+//// Msg Element(Msg)
+//// +--------+ v +----------------+ v +------+
+//// | | <-------------- | | <-------------- | |
+//// | update | | Lustre runtime | | view |
+//// | | --------------> | | --------------> | |
+//// +--------+ ^ +----------------+ ^ +------+
+//// #(model, Effect(msg)) | ^ Model
+//// | |
+//// | |
+//// DOM patches | | DOM events
+//// | |
+//// v |
+//// +-----------------------+
+//// | |
+//// | Your WebSocket server |
+//// | |
+//// +-----------------------+
+//// | ^
+//// | |
+//// DOM patches | | DOM events
+//// | |
+//// v |
+//// -- BROWSER ----------------------------------------------------------------
+//// | ^
+//// | |
+//// DOM patches | | DOM events
+//// | |
+//// v |
+//// +----------------+
+//// | |
+//// | Client runtime |
+//// | |
+//// +----------------+
+//// ```
+////
+//// **Note**: Lustre's server component runtime is separate from your application's
+//// WebSocket server. You're free to bring your own stack, connect multiple
+//// clients to the same Lustre instance, or keep the application alive even when
+//// no clients are connected.
+////
+//// Lustre server components run next to the rest of your backend code, your
+//// services, your database, etc. Real-time applications like chat services, games,
+//// or components that can benefit from direct access to your backend services
+//// like an admin dashboard or data table are excellent candidates for server
+//// components.
+////
+//// ## Examples
+////
+//// Server components are a new feature in Lustre and we're still working on the
+//// best ways to use them and show them off. For now, you can find a simple
+//// undocumented example in the `examples/` directory:
+////
+//// - [`99-server-components`](https://github.com/lustre-labs/lustre/tree/main/examples/99-server-components)
+////
+//// ## Getting help
+////
+//// If you're having trouble with Lustre or not sure what the right way to do
+//// something is, the best place to get help is the [Gleam Discord server](https://discord.gg/Fm8Pwmy).
+//// You could also open an issue on the [Lustre GitHub repository](https://github.com/lustre-labs/lustre/issues).
+////
// IMPORTS ---------------------------------------------------------------------
@@ -25,7 +92,11 @@ import lustre/internals/patch
// ELEMENTS --------------------------------------------------------------------
-/// A simple wrapper to render a `<lustre-server-component>` element.
+/// Render the Lustre Server Component client runtime. The content of your server
+/// component will be rendered inside this element.
+///
+/// **Note**: you must include the `lustre-server-component.mjs` script found in
+/// the `priv/` directory of the Lustre package in your project's HTML.
///
pub fn component(attrs: List(Attribute(msg))) -> Element(msg) {
element("lustre-server-component", attrs, [])
@@ -33,10 +104,10 @@ pub fn component(attrs: List(Attribute(msg))) -> Element(msg) {
// ATTRIBUTES ------------------------------------------------------------------
-/// The `route` attribute should always be included on a [`component`](#component)
-/// to tell the client runtime what path to initiate the WebSocket connection on.
-///
-///
+/// The `route` attribute tells the client runtime what route it should use to
+/// set up the WebSocket connection to the server. Whenever this attribute is
+/// changed (by a clientside Lustre app, for example), the client runtime will
+/// destroy the current connection and set up a new one.
///
pub fn route(path: String) -> Attribute(msg) {
attribute("route", path)
@@ -46,9 +117,8 @@ pub fn route(path: String) -> Attribute(msg) {
/// This could be used to include a hash of the current build to detect if the
/// event was sent from a stale client.
///
-/// ```gleam
-///
-/// ```
+/// Your event decoders can access this data by decoding `data` property of the
+/// event object.
///
pub fn data(json: Json) -> Attribute(msg) {
json
@@ -56,9 +126,9 @@ pub fn data(json: Json) -> Attribute(msg) {
|> attribute("data-lustre-data", _)
}
-/// Properties of the JavaScript event object are typically not serialisable.
-/// This means if we want to pass them to the server we need to copy them into
-/// a new object first.
+/// Properties of a JavaScript event object are typically not serialisable. This
+/// means if we want to pass them to the server we need to copy them into a new
+/// object first.
///
/// This attribute tells Lustre what properties to include. Properties can come
/// from nested objects by using dot notation. For example, you could include the
@@ -95,9 +165,8 @@ pub fn include(properties: List(String)) -> Attribute(msg) {
// ACTIONS ---------------------------------------------------------------------
-/// A [`ServerComponent`](../lustre#ServerComponent) broadcasts patches to be applied
-/// to the DOM to any connected clients. This action is used to add a new client
-/// to a running server component.
+/// A server component broadcasts patches to be applied to the DOM to any connected
+/// clients. This action is used to add a new client to a running server component.
///
pub fn subscribe(
id: String,
@@ -115,7 +184,13 @@ pub fn unsubscribe(id: String) -> Action(msg, ServerComponent) {
// EFFECTS ---------------------------------------------------------------------
+/// Instruct any connected clients to emit a DOM event with the given name and
+/// data. This lets your server component communicate to frontend the same way
+/// any other HTML elements do: you might emit a `"change"` event when some part
+/// of the server component's state changes, for example.
///
+/// This is a real DOM event and any JavaScript on the page can attach an event
+/// listener to the server component element and listen for these events.
///
pub fn emit(event: String, data: Json) -> Effect(msg) {
effect.event(event, data)
@@ -142,7 +217,10 @@ fn do_set_selector(_sel: Selector(Action(runtime, msg))) -> Effect(msg) {
// DECODERS --------------------------------------------------------------------
-///
+/// The server component client runtime sends JSON encoded actions for the server
+/// runtime to execute. Because your own WebSocket server sits between the two
+/// parts of the runtime, you need to decode these actions and pass them to the
+/// server runtime yourself.
///
pub fn decode_action(
dyn: Dynamic,
@@ -194,7 +272,9 @@ fn decode_attr(dyn: Dynamic) -> Result(#(String, Dynamic), List(DecodeError)) {
// ENCODERS --------------------------------------------------------------------
-///
+/// Encode a DOM patch as JSON you can send to the client runtime to apply. Whenever
+/// the server runtime re-renders, all subscribed clients will receive a patch
+/// message they must forward to the client runtime.
///
pub fn encode_patch(patch: Patch(msg)) -> Json {
patch.patch_to_json(patch)