howdy/dev
Hot reload for development.
Put this in dev/<app>_dev.gleam and run it with gleam dev:
import howdy/dev
import my_app
pub fn main() {
dev.start(my_app.app)
}
where my_app.app is the function that builds your howdy.App. Pass
it by name rather than calling it: the server rebuilds the app from it
for every request, so a reloaded module is used at once.
While it runs, every change under src triggers gleam build. If the
build succeeds the changed modules are loaded into the running VM and
every open page reloads. If it fails the compiler output appears in the
terminal and over the page, and the old code keeps serving.
Generating code before a build
before_build runs a task of your own before each build, such as
regenerating messages when a catalogue changes:
import howdy/i18n/cli
pub fn main() {
let assert Ok(_) =
dev.new(my_app.app)
|> dev.watch("locales")
|> dev.before_build(fn(_changed) { cli.execute(["gen"]) })
|> dev.run
process.sleep_forever()
}
A task that fails stops the build, and its output is shown like a compiler error.
Staying out of production
howdy_dev is a dev dependency and the entry point lives in dev/.
gleam export erlang-shipment includes neither, so a deployed app has
no reload code in it. gleam run starts src/<app>.gleam as usual.
Limits
Processes started once in main, such as a shared live runtime, keep
running the code they were started with. They are ended when that code
is purged, two reloads later. Restart gleam dev after changing them.
Changes to dev/, test/, gleam.toml or dependencies also need a
restart. Files are polled four times a second, so a change is noticed
within a quarter of a second.
Types
Values
pub fn allow_hosts(dev: Dev, hosts: List(String)) -> Dev
Exact request hostnames (without port) allowed to read development pages
and connect to reload. This prevents hostile DNS-rebinding hosts from
obtaining the reload token. Include brackets for IPv6, e.g. [::1].
pub fn before_build(
dev: Dev,
task: fn(List(String)) -> Result(String, String),
) -> Dev
Run task before each build, with the files that changed. Its output is
printed; an Error stops the build and is shown over the page like a
compiler error. Files a task writes do not trigger another build. Tasks
run in the order they are added.
pub fn bind(dev: Dev, to address: String) -> Dev
Override the development listener’s loopback default. Network exposure
is explicit: also list the hostname/IP browsers use with allow_hosts.
pub fn interval(dev: Dev, milliseconds: Int) -> Dev
How often to look for changes, in milliseconds. Defaults to 250.
pub fn new(build: fn() -> howdy.App) -> Dev
Describe a development server for the app build makes. Watches src.
pub fn run(
dev: Dev,
) -> Result(actor.Started(howdy.Address), actor.StartError)
Start the server and the file watcher. Returns like howdy.start; keep
the calling process alive with process.sleep_forever().
pub fn start(
build: fn() -> howdy.App,
) -> Result(actor.Started(howdy.Address), actor.StartError)
new(build) |> start.