Kevin Tale
Kevin Taleβ€’2w 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β€’2w 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β€’2w 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β€’2w ago
Oh yeah makes sense, it's because it watches the ts file, not the html one sorry
Kevin Tale
Kevin TaleOPβ€’2w ago
yeah it seems tricky. I thought about using websocket but couldn't fingure out how
2saturdayscode
2saturdayscodeβ€’2w 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β€’2w ago
awesome, thanks man!
2saturdayscode
2saturdayscodeβ€’2w ago
Here I am This is the server part: Message too long 😦
2saturdayscode
2saturdayscodeβ€’2w 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β€’2w 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β€’2w ago
You are most welcome, I understand the excitement it’s so satisfying building stuff from scratch
Fifth-Normal-Form always
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β€’2w ago
Very interesting I'll give it a proper look, thanks!
Fifth-Normal-Form always
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β€’2w ago
Did you made this package ?
Fifth-Normal-Form always
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β€’2w 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 always
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β€’2w ago
That's a nice idea! Just inject a script tag nice, it also encapsulate the behavior
Fifth-Normal-Form always
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