Kevin Tale
Kevin Taleβ€’5w ago

How to add a simple live reload?

Consider this super simple codebase
app/
index.html
main.ts
app/
index.html
main.ts
// main.ts
const content = Deno.readTextFileSync("./index.html");

Deno.serve(() => new Response(content, {
headers: { "Content-Type": "text/html" },
})
);
// main.ts
const content = Deno.readTextFileSync("./index.html");

Deno.serve(() => new Response(content, {
headers: { "Content-Type": "text/html" },
})
);
// index.html
<p>Hello world</p>
// index.html
<p>Hello world</p>
Then I run deno run -A -r --watch main.ts. http://localhost:8000/ will correctly serve my html content but how can I enable somekind of live reload where if I edit my index.html in VSCode it will effectively show the updated index.html in my browser? thanks
20 Replies
2saturdayscode
2saturdayscodeβ€’5w ago
Quickest path would be to get rid of Deno.serve and instead just export a default function like this:
// main.ts
const content = Deno.readTextFileSync("./index.html")

export default {
async fetch(request) {

return new Response(content, {
headers: { "Content-Type": "text/html" },
})
},
};
// main.ts
const content = Deno.readTextFileSync("./index.html")

export default {
async fetch(request) {

return new Response(content, {
headers: { "Content-Type": "text/html" },
})
},
};
Then simply run the cli serve command:
deno serve --watch --port 8000 main.ts
deno serve --watch --port 8000 main.ts
Here's the docs about it: https://docs.deno.com/runtime/reference/cli/serve/
Deno
deno serve
In-depth documentation, guides, and reference materials for building secure, high-performance JavaScript and TypeScript applications with Deno
Kevin Tale
Kevin TaleOPβ€’5w ago
thanks for the help. It kinda helped but I dont have the expected behavior yet. I've modified the code like so
export default {
async fetch() {
console.log(Math.random());
const content = Deno.readTextFileSync("./index.html");

return new Response(content, {
headers: { "Content-Type": "text/html" },
});
},
};
export default {
async fetch() {
console.log(Math.random());
const content = Deno.readTextFileSync("./index.html");

return new Response(content, {
headers: { "Content-Type": "text/html" },
});
},
};
And tried that deno serve -A -r --watch main.ts or that deno serve -A -r --watch=index.html main.ts And it seems that the Math.random() will only trigger the first time localhost:8000 is requested (so basically when my browser access this url). But on file change it wont reload it even though I can indeed see the message Watcher Restarting! File change detected: "C:\\Users\\33646\\Documents\\labs\\deno\\main.ts" in my console The Math.random() will trigger again when I F5 my browser which is not what I want
2saturdayscode
2saturdayscodeβ€’5w ago
Oh yeah makes sense, it's because it watches the ts file, not the html one sorry
Kevin Tale
Kevin TaleOPβ€’5w ago
yeah it seems tricky. I thought about using websocket but couldn't fingure out how
2saturdayscode
2saturdayscodeβ€’5w ago
I don't think there's a "native" way to do it, here's how I do it using SSE Give me a couple of minutes to extract the interesting part (I still need to clean this file) and add some comments for explanation
Kevin Tale
Kevin TaleOPβ€’5w ago
awesome, thanks man!
2saturdayscode
2saturdayscodeβ€’5w ago
Here I am This is the server part: Message too long 😦
2saturdayscode
2saturdayscodeβ€’5w ago
On the client side, in a script tag of your index.html you should add:
const src = new EventSource('/live-reload')

src.addEventListener('message', (e) => {
if (e.data === 'reload') {
src.close()
location.reload()
}
})

src.addEventListener('open', () => {
console.log('Live reload connected!')
})
const src = new EventSource('/live-reload')

src.addEventListener('message', (e) => {
if (e.data === 'reload') {
src.close()
location.reload()
}
})

src.addEventListener('open', () => {
console.log('Live reload connected!')
})
Kevin Tale
Kevin TaleOPβ€’5w ago
Thanks I'll look at this tomorrow for sure and let you know πŸ™‚ ok perfect, I've managed to make it work πŸ™‚ I've used websocket which is similar but we some differences on the syntax. Now I can build my hmr system! Thanks again @2saturdayscode !
2saturdayscode
2saturdayscodeβ€’5w ago
You are most welcome, I understand the excitement it’s so satisfying building stuff from scratch
Fifth-Normal-Form
Fifth-Normal-Formβ€’5w ago
An example WebSocket refresh that is injected on serve, and does not modify the html file. It simply modifies the text sent to the browser. https://github.com/nhrones/Devtools_Hot/blob/main/injector.ts This server auto-starts the browser, then on any ./src/ or ./dist/ change, it builds/bundles and then auto-refreshes the browser.
It injects the WebSocket code (above) whenever the request URL is for '/' or 'index.html' https://github.com/nhrones/Devtools_Hot/blob/main/server.ts The unique feature is that the watch-function will distinguish between a code change or a stylesheet change. For a stylesheet, it simply refreshes the styles without disturbing the app.
Kevin Tale
Kevin TaleOPβ€’5w ago
Very interesting I'll give it a proper look, thanks!
Fifth-Normal-Form
Fifth-Normal-Formβ€’5w ago
It is a bit dated, but still works great for me. I'm currently moving all my utilities to JSR so there may be broken links. I have Hot installed locally and use it often.
Kevin Tale
Kevin TaleOPβ€’5w ago
Did you made this package ?
Fifth-Normal-Form
Fifth-Normal-Formβ€’5w ago
Yes! But long agoπŸ˜† I just put it back in a public repo, so that I can move all my junk to JSR.
Kevin Tale
Kevin TaleOPβ€’5w ago
so if I understand correctly your library allow to add hot reload functionality without having to manually add the websocket code to our index.html? It also recompile if src/ has change and simply reload the browser if the webapp per se changes (or just change the stylesheet if css files have change). Is that correct?
Fifth-Normal-Form
Fifth-Normal-Formβ€’5w ago
Yes! Whenever index.html is requested, we inject the WebSocket code to the end of the text being sent to the browser. The file itself is never modified. Any .ts-code change forces a new build/bundle to ./dist/bundle.js . This new bundle, or any change to the html or CSS files force either a globalThis.location.reload() or a remove-then-reinstall stylesheets style-refresh by way of the WebSocket. Be aware that I'm making many link changes to the code as I move all utilities to JSR. The config utility is in flux as well as the builder utility. Its all there in public repos, but as I untangle the local references to move all to JSR, it may not work without reference errors. I got lazy with these and used local refs for the dependencies. I'll have it all moved in a day or two. The auto-config is not required. I use it in all dev-tools because I'm lazy. I like to just type 'serve' or 'hot' in any project to get coding. I'm just a hobbyist trying to learn Web-Dev.
2saturdayscode
2saturdayscodeβ€’5w ago
That's a nice idea! Just inject a script tag nice, it also encapsulate the behavior
Fifth-Normal-Form
Fifth-Normal-Formβ€’4w ago
in the server ...
if (isIndexHtml) {
// inject html with our hot refresh script
const body = await inject(fullPath)
// create appropriate headers
const headers = new Headers()
headers.set("content-type", "text/html; charset=utf-8")
// don't cache this - we expect frequent dev changes
headers.append("Cache-Control", "no-store")
return new Response(body, { status: 200, headers });
if (isIndexHtml) {
// inject html with our hot refresh script
const body = await inject(fullPath)
// create appropriate headers
const headers = new Headers()
headers.set("content-type", "text/html; charset=utf-8")
// don't cache this - we expect frequent dev changes
headers.append("Cache-Control", "no-store")
return new Response(body, { status: 200, headers });
Now on JSR https://jsr.io/@ndh/hot@1.0.0