Skip to content

Commit

Permalink
Add live reloading example (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmattio authored May 3, 2021
1 parent 98004de commit adab8c4
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 0 deletions.
17 changes: 17 additions & 0 deletions example/w-live-reloading/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# `w-live-reload`

<br>

This example demonstrates how to setup live reloading of the client HTML content.

It works by injecting a script in the HTML pages sent to clients that will initiate a WebSocket.

When the server restarts, the WebSocket connection is lost, at which point, the client will try to reconnect every 500ms for 5s.
If within these 5s the client is able to reconnect to the server, it will trigger a reload of the page.

This example plays very well with `w-fswatch`, which demonstrates how to restart the server every time the filesystem is modified.
When integrating the two examples, one is able to have a setup where the clients' pages are reloaded every time a file is modified.

<br>

[Up to the example index](../#examples)
4 changes: 4 additions & 0 deletions example/w-live-reloading/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(executable
(name live_reloading)
(libraries dream lambdasoup)
(preprocess (pps lwt_ppx)))
1 change: 1 addition & 0 deletions example/w-live-reloading/dune-project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(lang dune 2.0)
10 changes: 10 additions & 0 deletions example/w-live-reloading/esy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"dependencies": {
"@opam/dream": "aantron/dream:dream.opam",
"@opam/dune": "^2.0",
"ocaml": "4.12.x"
},
"scripts": {
"start": "dune exec --root . ./live_reloading.exe"
}
}
89 changes: 89 additions & 0 deletions example/w-live-reloading/live_reloading.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
let livereload_script
?(retry_interval_ms = 500)
?(max_retry_ms = 5000)
?(route = "/_livereload")
()
=
Printf.sprintf
{js|
var socketUrl = "ws://" + location.host + "%s"
var s = new WebSocket(socketUrl);

s.onopen = function(even) {
console.log("WebSocket connection open.");
};

s.onclose = function(even) {
console.log("WebSocket connection closed.");
const innerMs = %i;
const maxMs = %i;
const maxAttempts = Math.round(maxMs / innerMs);
let attempts = 0;
function reload() {
attempts++;
if(attempts > maxAttempts) {
console.error("Could not reconnect to dev server.");
return;
}

s2 = new WebSocket(socketUrl);

s2.onerror = function(event) {
setTimeout(reload, innerMs);
};

s2.onopen = function(event) {
location.reload();
};
};
reload();
};

s.onerror = function(event) {
console.error("WebSocket error observed:", event);
};
|js}
route
retry_interval_ms
max_retry_ms

let inject_livereload_script
?(reload_script = livereload_script ())
()
(next_handler : Dream.request -> Dream.response Lwt.t)
(request : Dream.request)
: Dream.response Lwt.t
=
let%lwt response = next_handler request in
match Dream.header "Content-Type" response with
| Some "text/html" | Some "text/html; charset=utf-8" ->
let%lwt body = Dream.body response in
let soup = Soup.parse body in
let open Soup.Infix in
(match soup $? "head" with
| None ->
Lwt.return response
| Some head ->
Soup.create_element "script" ~inner_text:reload_script
|> Soup.append_child head;
Lwt.return (Dream.with_body (Soup.to_string soup) response))
| _ ->
Lwt.return response

let livereload_route ?(path = "/_livereload") () =
Dream.get path (fun _ ->
Dream.websocket (fun socket ->
Lwt.bind (Dream.receive socket) (fun _ ->
Dream.close_websocket socket)))

let () =
Dream.run
@@ Dream.logger
@@ inject_livereload_script ()
@@ Dream.router [
livereload_route ();
Dream.get "/"
(fun _ ->
Dream.html "Good morning, world!");
]
@@ Dream.not_found

0 comments on commit adab8c4

Please sign in to comment.