Packages
lustre
3.1.4
5.7.1
5.7.0
5.6.0
5.5.2
5.5.1
5.5.0
5.4.0
5.3.5
5.3.4
5.3.3
5.3.2
5.3.1
5.3.0
5.2.1
5.2.0
5.1.1
5.1.0
5.0.3
5.0.2
5.0.1
5.0.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.8
4.1.7
4.1.6
4.1.5
4.1.4
4.1.3
4.1.2
4.1.1
4.1.0
4.0.0
4.0.0-rc1
4.0.0-rc.2
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
2.0.1
2.0.0
1.3.0
1.2.0
1.1.0
1.0.0
Create HTML templates, single page applications, Web Components, and real-time server components in Gleam!
Current section
Files
Jump to
Current section
Files
src/lustre.gleam
//// To read the full documentation for this module, please visit
//// [https://lustre.build/api/lustre](https://lustre.build/api/lustre)
// IMPORTS ---------------------------------------------------------------------
import gleam/dynamic.{type Decoder}
import gleam/dict.{type Dict}
import lustre/effect.{type Effect}
import lustre/element.{type Element}
// TYPES -----------------------------------------------------------------------
@target(javascript)
///
pub type App(flags, model, msg)
@target(erlang)
///
pub opaque type App(flags, model, msg) {
App
}
pub type Error {
AppAlreadyStarted
AppNotYetStarted
BadComponentName
ComponentAlreadyRegistered
ElementNotFound
NotABrowser
}
// CONSTRUCTORS ----------------------------------------------------------------
///
pub fn element(element: Element(msg)) -> App(Nil, Nil, msg) {
let init = fn(_) { #(Nil, effect.none()) }
let update = fn(_, _) { #(Nil, effect.none()) }
let view = fn(_) { element }
application(init, update, view)
}
///
pub fn simple(
init: fn(flags) -> model,
update: fn(model, msg) -> model,
view: fn(model) -> Element(msg),
) -> App(flags, model, msg) {
let init = fn(flags) { #(init(flags), effect.none()) }
let update = fn(model, msg) { #(update(model, msg), effect.none()) }
application(init, update, view)
}
///
@external(javascript, "./lustre.ffi.mjs", "setup")
pub fn application(
_init: fn(flags) -> #(model, Effect(msg)),
_update: fn(model, msg) -> #(model, Effect(msg)),
_view: fn(model) -> Element(msg),
) -> App(flags, model, msg) {
// Applications are not usable on the erlang target. For those users, `App`
// is an opaque type (aka they can't see its structure) and functions like
// `start` and `destroy` are no-ops.
//
// Because the constructor is marked as `@target(erlang)` for some reason we
// can't simply refer to it here even though the compiler should know that the
// body of this function can only be entered from erlang (because we have an
// external def for javascript) but alas, it does not.
//
// So instead, we must do this awful hack and cast a `Nil` to the `App` type
// to make everything happy. Theoeretically this is not going to be a problem
// unless someone starts poking around with their own ffi and at that point
// they deserve it.
dynamic.unsafe_coerce(dynamic.from(Nil))
}
@external(javascript, "./lustre.ffi.mjs", "setup_component")
pub fn component(
_name: String,
_init: fn() -> #(model, Effect(msg)),
_update: fn(model, msg) -> #(model, Effect(msg)),
_view: fn(model) -> Element(msg),
_on_attribute_change: Dict(String, Decoder(msg)),
) -> Result(Nil, Error) {
Ok(Nil)
}
// EFFECTS ---------------------------------------------------------------------
///
@external(javascript, "./lustre.ffi.mjs", "start")
pub fn start(
_app: App(flags, model, msg),
_selector: String,
_flags: flags,
) -> Result(fn(msg) -> Nil, Error) {
Error(NotABrowser)
}
///
@external(javascript, "./lustre.ffi.mjs", "destroy")
pub fn destroy(_app: App(flags, model, msg)) -> Result(Nil, Error) {
Ok(Nil)
}
// UTILS -----------------------------------------------------------------------
///
@external(javascript, "./lustre.ffi.mjs", "is_browser")
pub fn is_browser() -> Bool {
False
}
///
@external(javascript, "./lustre.ffi.mjs", "is_registered")
pub fn is_registered(_name: String) -> Bool {
False
}