mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
vweb: implement live page reload in development, based on polling (useful with watch) (#17683)
This commit is contained in:
@@ -167,7 +167,8 @@ pub mut:
|
||||
|
||||
header http.Header // response headers
|
||||
// ? It doesn't seem to be used anywhere
|
||||
form_error string
|
||||
form_error string
|
||||
livereload_poll_interval_ms int = 250
|
||||
}
|
||||
|
||||
struct FileData {
|
||||
@@ -190,8 +191,8 @@ pub fn (ctx Context) init_server() {
|
||||
}
|
||||
|
||||
// Defining this method is optional.
|
||||
// This method called before every request (aka middleware).
|
||||
// Probably you can use it for check user session cookie or add header.
|
||||
// This method is called before every request (aka middleware).
|
||||
// You can use it for checking user session cookies or to add headers.
|
||||
pub fn (ctx Context) before_request() {}
|
||||
|
||||
// TODO - test
|
||||
@@ -202,17 +203,22 @@ pub fn (mut ctx Context) send_response_to_client(mimetype string, res string) bo
|
||||
return false
|
||||
}
|
||||
ctx.done = true
|
||||
|
||||
// build header
|
||||
header := http.new_header_from_map({
|
||||
http.CommonHeader.content_type: mimetype
|
||||
http.CommonHeader.content_length: res.len.str()
|
||||
}).join(ctx.header)
|
||||
|
||||
//
|
||||
mut resp := http.Response{
|
||||
header: header.join(vweb.headers_close)
|
||||
body: res
|
||||
}
|
||||
$if vweb_livereload ? {
|
||||
if mimetype == 'text/html' {
|
||||
resp.body = res.replace('</html>', '<script src="/vweb_livereload/${vweb_livereload_server_start}/script.js"></script>\n</html>')
|
||||
}
|
||||
}
|
||||
// build the header after the potential modification of resp.body from above
|
||||
header := http.new_header_from_map({
|
||||
http.CommonHeader.content_type: mimetype
|
||||
http.CommonHeader.content_length: resp.body.len.str()
|
||||
}).join(ctx.header)
|
||||
resp.header = header.join(vweb.headers_close)
|
||||
//
|
||||
resp.set_version(.v1_1)
|
||||
resp.set_status(http.status_from_int(ctx.status.int()))
|
||||
send_string(mut ctx.conn, resp.bytestr()) or { return false }
|
||||
@@ -504,6 +510,19 @@ fn handle_conn[T](mut conn net.TcpConn, mut app T, routes map[string]Route) {
|
||||
// Calling middleware...
|
||||
app.before_request()
|
||||
|
||||
$if vweb_livereload ? {
|
||||
if url.path.starts_with('/vweb_livereload/') {
|
||||
if url.path.ends_with('current') {
|
||||
app.handle_vweb_livereload_current()
|
||||
return
|
||||
}
|
||||
if url.path.ends_with('script.js') {
|
||||
app.handle_vweb_livereload_script()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static handling
|
||||
if serve_if_static[T](mut app, url) {
|
||||
// successfully served a static file
|
||||
|
||||
48
vlib/vweb/vweb_livereload.v
Normal file
48
vlib/vweb/vweb_livereload.v
Normal file
@@ -0,0 +1,48 @@
|
||||
module vweb
|
||||
|
||||
import time
|
||||
|
||||
// Note: to use live reloading while developing, the suggested workflow is doing:
|
||||
// `v -d vweb_livereload watch --keep run your_vweb_server_project.v`
|
||||
// in one shell, then open the start page of your vweb app in a browser.
|
||||
//
|
||||
// While developing, just open your files and edit them, then just save your
|
||||
// changes. Once you save, the watch command from above, will restart your server,
|
||||
// and your HTML pages will detect that shortly, then they will refresh themselves
|
||||
// automatically.
|
||||
|
||||
// vweb_livereload_server_start records, when the vweb server process started.
|
||||
// That is later used by the /script.js and /current endpoints, which are active,
|
||||
// if you have compiled your vweb project with `-d vweb_livereload`, to detect
|
||||
// whether the web server has been restarted.
|
||||
const vweb_livereload_server_start = time.ticks().str()
|
||||
|
||||
// handle_vweb_livereload_current serves a small text file, containing the
|
||||
// timestamp/ticks corresponding to when the vweb server process was started
|
||||
[if vweb_livereload ?]
|
||||
fn (mut ctx Context) handle_vweb_livereload_current() {
|
||||
ctx.send_response_to_client('text/plain', vweb.vweb_livereload_server_start)
|
||||
}
|
||||
|
||||
// handle_vweb_livereload_script serves a small dynamically generated .js file,
|
||||
// that contains code for polling the vweb server, and reloading the page, if it
|
||||
// detects that the vweb server is newer than the vweb server, that served the
|
||||
// .js file originally.
|
||||
[if vweb_livereload ?]
|
||||
fn (mut ctx Context) handle_vweb_livereload_script() {
|
||||
res := '"use strict";
|
||||
function vweb_livereload_checker_fn(started_at) {
|
||||
fetch("/vweb_livereload/" + started_at + "/current", { cache: "no-cache" })
|
||||
.then(response=>response.text())
|
||||
.then(function(current_at) {
|
||||
// console.log(started_at); console.log(current_at);
|
||||
if(started_at !== current_at){
|
||||
// the app was restarted on the server:
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
const vweb_livereload_checker = setInterval(vweb_livereload_checker_fn, ${ctx.livereload_poll_interval_ms}, "${vweb.vweb_livereload_server_start}");
|
||||
'
|
||||
ctx.send_response_to_client('text/javascript', res)
|
||||
}
|
||||
Reference in New Issue
Block a user