-
-
Notifications
You must be signed in to change notification settings - Fork 132
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
121 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
(lang dune 2.0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |