Readme

Live reload in new tabs

When you're working on an HTML HTTP val in a new tab, it's annoying to have to manually reload the tab on every save. In the Val Town editor, you can hit cmd+enter, but there's nothing like that for a val in a new tab because Val Town doesn't control that new tab (like we control the iframe in the browser preview). However, you control that HTML via the fetch handler you're writing, so you can add a script that polls the Val Town API for the current version number of your val, and reload itself if it detects a new version. This val has a collection of helpers to help you do just that.

Usage

import { html } from "https://esm.town/v/stevekrouse/html"; import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave"; export default reloadOnSaveFetchMiddleware(async function(req: Request): Promise<Response> { return html(`<h1>Hello!!</h1>`); })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { InjectHTMLElementStream } from "https://esm.town/v/andreterron/InjectHTMLElementStream?v=9";
import { rootValRef } from "https://esm.town/v/andreterron/rootValRef?v=3";
import { ValRef } from "https://esm.town/v/andreterron/ValRef?v=1";
import { getCurrentValVersionNumber } from "https://esm.town/v/stevekrouse/getCurrentValVersionNumber";
import { modifyResponse } from "https://esm.town/v/stevekrouse/modifyResponse";
import { parentReference } from "https://esm.town/v/stevekrouse/parentReference";
import type { MiddlewareHandler } from "npm:hono";
// this script runs client-side
export const reloadOnVals = async function(vals: ValRef[]) {
const valVersions = await Promise.all(vals.map(getCurrentValVersionNumber));
// console.log("initialValVersions: ", valVersions);
const interval = setInterval(async () => {
let newValVersions = await Promise.all(vals.map(getCurrentValVersionNumber));
// console.log("newValVersions: ", newValVersions);
if (JSON.stringify(newValVersions) !== JSON.stringify(valVersions)) {
clearInterval(interval);
window.location.reload();
}
}, 1000);
};
// experimental - don't use this
export function reloadOnSaveHonoMiddleware(vals: ValRef[] = [rootValRef()]): MiddlewareHandler {
return async function(c, next) {
await next();
// don't reload on custom domains, only reload on val.run URLs
if (!new URL(c.req.url).hostname.includes("val.run")) {
return;
}
c.res = modifyResponse(
c.res,
new InjectHTMLElementStream(ReloadScriptText(vals)),
);
};
}
/**
* @param handler http val's fetch handler
* @param vals to watch
*/
export function reloadOnSaveFetchMiddleware(
handler: (req: Request) => Response | Promise<Response>,
vals = [rootValRef()],
): (req: Request) => Promise<Response> {
return async (req: Request): Promise<Response> => {
const res = await handler(req);
// don't reload on custom domains, only reload on val.run URLs
if (!new URL(req.url).hostname.includes("val.run")) {
return res;
}
return modifyResponse(
res,
new InjectHTMLElementStream(ReloadScriptText(vals)),
);
};
}
// generates a script element as a string
export const ReloadScriptText = (vals = [rootValRef()]) =>
`<script type="module">
import { reloadOnVals } from "${import.meta.url}"
reloadOnVals(${JSON.stringify(vals)})
</script>`;
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
August 25, 2024