From d06db4c3fdc01b88b14c0f47ab9a32684f874fbd Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Mon, 25 Mar 2024 23:34:06 +0000 Subject: :memo: Write some basic documentation on what server components are all about. --- src/lustre/server_component.gleam | 112 ++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 16 deletions(-) (limited to 'src') 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 `` 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) -- cgit v1.2.3