UNPKG

hotweby

Version:

Automatic hot-reloading webserver using websockets

138 lines 19.1 kB
import express from "express"; import * as afs from "fs/promises"; import * as path from "path"; import { WebSocket, WebSocketServer } from "ws"; export function createReloadHtmlCode() { const reloadHtmlScript = async () => { console.log("[HOTWEBY]: connect websocket..."); const connectWs = () => { const wsUrl = (location.protocol === "https:" ? "wss://" : "ws://") + location.host; const ws = new WebSocket(wsUrl); ws.onopen = () => console.log("[HOTWEBY]: websocket connected"); ws.onclose = () => { console.log("[HOTWEBY]: websocket closed, reloading..."); setTimeout(() => location.reload(), 100); }; ws.onerror = err => { console.error("[HOTWEBY]: websocket error, reloading...", err); setTimeout(() => location.reload(), 1000); }; }; connectWs(); }; const reloadHtmlCode = "" + reloadHtmlScript; return "<script>\n(" + reloadHtmlCode + ")()\n</script>"; } export function createExpress(targetDir, reloadHtmlCode, autoExtensionResolution, verbose) { const app = express(); app.use(async (req, res, next) => { if (!req.path.endsWith(".html") && !req.path.endsWith("/")) { return next(); } let reqPath = req.path; if (reqPath.endsWith("/")) { reqPath += "index.html"; } try { const data = await afs.readFile(targetDir + reqPath, "utf8"); res.status(200); res.send(reloadHtmlCode + "\n" + data.toString()); verbose && console.info("Served html-file '" + req.path + "' from '" + targetDir + reqPath + "'"); } catch (err) { console.error("Cant read requested html-file '" + req.path + "'" + "\nfrom '" + targetDir + reqPath + "':\n", err); res.status(503); res.setHeader("Content-Type", "text/plain"); res.setHeader("Retry-After", "5"); res.send("Cant read requested html-file '" + req.path + "'"); } }); autoExtensionResolution && app.use(async (req, res, next) => { const resolvedExtension = await autoResolveExtensions(req.url, targetDir, verbose); if (resolvedExtension) { req.url = resolvedExtension; } next(); }); app.use(express.static(targetDir)); return app; } export async function autoResolveExtensions(reqUrl, targetDir, verbose) { while (reqUrl.startsWith("/")) { reqUrl = reqUrl.slice(1); } if (reqUrl === "") { return undefined; } try { let realPath = targetDir + "/" + reqUrl; if ((await pathType(realPath)) === "none") { const files = await afs.readdir(path.dirname(realPath)); const baseName = path.basename(realPath); for (const file of files) { if (file.startsWith(baseName + ".")) { verbose && console.info("Auto resolved extension for '" + reqUrl + "' to be '" + path.dirname(reqUrl) + "/" + file + "'"); return path.dirname(reqUrl) + "/" + file; } } } } catch (err) { verbose && console.error("Error while auto resolving extension for " + reqUrl + "\n", err); } return undefined; } export async function pathType(path) { try { const stat = await afs.stat(path); return stat.isFile() ? "file" : "dir"; } catch { } return "none"; } export function createWebSocketServer(httpServer, registerTrigger) { const wsServer = new WebSocketServer({ noServer: true }); wsServer.on("connection", (ws, req) => { registerTrigger(() => { if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) { ws.close(); } }); }); httpServer.on("upgrade", (req, socket, head) => { wsServer.handleUpgrade(req, socket, head, ws => { wsServer.emit("connection", ws, req); }); }); return wsServer; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE9BQU8sTUFBTSxTQUFTLENBQUE7QUFDN0IsT0FBTyxLQUFLLEdBQUcsTUFBTSxhQUFhLENBQUE7QUFFbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUE7QUFDNUIsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLEVBQUUsTUFBTSxJQUFJLENBQUE7QUFHL0MsTUFBTSxVQUFVLG9CQUFvQjtJQUNoQyxNQUFNLGdCQUFnQixHQUFHLEtBQUssSUFBSSxFQUFFO1FBQ2hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUNBQWlDLENBQUMsQ0FBQTtRQUU5QyxNQUFNLFNBQVMsR0FBRyxHQUFHLEVBQUU7WUFDbkIsTUFBTSxLQUFLLEdBQ1AsQ0FBQyxRQUFRLENBQUMsUUFBUSxLQUFLLFFBQVE7Z0JBQzNCLENBQUMsQ0FBQyxRQUFRO2dCQUNWLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFBO1lBRWxDLE1BQU0sRUFBRSxHQUFHLElBQUksU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFBO1lBQy9CLEVBQUUsQ0FBQyxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQ2IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFBO1lBQ2pELEVBQUUsQ0FBQyxPQUFPLEdBQUcsR0FBRyxFQUFFO2dCQUNkLE9BQU8sQ0FBQyxHQUFHLENBQ1AsMkNBQTJDLENBQzlDLENBQUE7Z0JBQ0QsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQTtZQUM1QyxDQUFDLENBQUE7WUFDRCxFQUFFLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxFQUFFO2dCQUNmLE9BQU8sQ0FBQyxLQUFLLENBQ1QsMENBQTBDLEVBQzFDLEdBQUcsQ0FDTixD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QUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEVBQUUsQ0FBQyxFQUFFO1lBQzNDLFFBQVEsQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQTtRQUN4QyxDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUMsQ0FBQyxDQUFBO0lBRUYsT0FBTyxRQUFRLENBQUE7QUFDbkIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBleHByZXNzIGZyb20gXCJleHByZXNzXCJcbmltcG9ydCAqIGFzIGFmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIlxuaW1wb3J0IHsgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXIsIFNlcnZlclJlc3BvbnNlIH0gZnJvbSBcImh0dHBcIlxuaW1wb3J0ICogYXMgcGF0aCBmcm9tIFwicGF0aFwiXG5pbXBvcnQgeyBXZWJTb2NrZXQsIFdlYlNvY2tldFNlcnZlciB9IGZyb20gXCJ3c1wiXG5pbXBvcnQgeyBUcmlnZ2VySGFuZGxlciB9IGZyb20gXCIuL2luZGV4LmpzXCJcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVJlbG9hZEh0bWxDb2RlKCkge1xuICAgIGNvbnN0IHJlbG9hZEh0bWxTY3JpcHQgPSBhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiW0hPVFdFQlldOiBjb25uZWN0IHdlYnNvY2tldC4uLlwiKVxuXG4gICAgICAgIGNvbnN0IGNvbm5lY3RXcyA9ICgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHdzVXJsID1cbiAgICAgICAgICAgICAgICAobG9jYXRpb24ucHJvdG9jb2wgPT09IFwiaHR0cHM6XCJcbiAgICAgICAgICAgICAgICAgICAgPyBcIndzczovL1wiXG4gICAgICAgICAgICAgICAgICAgIDogXCJ3czovL1wiKSArIGxvY2F0aW9uLmhvc3RcblxuICAgICAgICAgICAgY29uc3Qgd3MgPSBuZXcgV2ViU29ja2V0KHdzVXJsKVxuICAgICAgICAgICAgd3Mub25vcGVuID0gKCkgPT5cbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcIltIT1RXRUJZXTogd2Vic29ja2V0IGNvbm5lY3RlZFwiKVxuICAgICAgICAgICAgd3Mub25jbG9zZSA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgXCJbSE9UV0VCWV06IHdlYnNvY2tldCBjbG9zZWQsIHJlbG9hZGluZy4uLlwiLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IGxvY2F0aW9uLnJlbG9hZCgpLCAxMDApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB3cy5vbmVycm9yID0gZXJyID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAgICAgICBcIltIT1RXRUJZXTogd2Vic29ja2V0IGVycm9yLCByZWxvYWRpbmcuLi5cIixcbiAgICAgICAgICAgICAgICAgICAgZXJyLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IGxvY2F0aW9uLnJlbG9hZCgpLCAxMDAwKVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgY29ubmVjdFdzKClcbiAgICB9XG5cbiAgICBjb25zdCByZWxvYWRIdG1sQ29kZSA9IFwiXCIgKyByZWxvYWRIdG1sU2NyaXB0XG5cbiAgICByZXR1cm4gXCI8c2NyaXB0PlxcbihcIiArIHJlbG9hZEh0bWxDb2RlICsgXCIpKClcXG48L3NjcmlwdD5cIlxufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRXhwcmVzcyhcbiAgICB0YXJnZXREaXI6IHN0cmluZyxcbiAgICByZWxvYWRIdG1sQ29kZTogc3RyaW5nLFxuICAgIGF1dG9FeHRlbnNpb25SZXNvbHV0aW9uOiBib29sZWFuLFxuICAgIHZlcmJvc2U6IGJvb2xlYW4sXG4pIHtcbiAgICBjb25zdCBhcHAgPSBleHByZXNzKClcblxuICAgIGFwcC51c2UoYXN5bmMgKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgICFyZXEucGF0aC5lbmRzV2l0aChcIi5odG1sXCIpICYmXG4gICAgICAgICAgICAhcmVxLnBhdGguZW5kc1dpdGgoXCIvXCIpXG4gICAgICAgICkge1xuICAgICAgICAgICAgcmV0dXJuIG5leHQoKVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IHJlcVBhdGggPSByZXEucGF0aFxuXG4gICAgICAgIGlmIChyZXFQYXRoLmVuZHNXaXRoKFwiL1wiKSkge1xuICAgICAgICAgICAgcmVxUGF0aCArPSBcImluZGV4Lmh0bWxcIlxuICAgICAgICB9XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCBhZnMucmVhZEZpbGUoXG4gICAgICAgICAgICAgICAgdGFyZ2V0RGlyICsgcmVxUGF0aCxcbiAgICAgICAgICAgICAgICBcInV0ZjhcIixcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHJlcy5zdGF0dXMoMjAwKVxuICAgICAgICAgICAgcmVzLnNlbmQocmVsb2FkSHRtbENvZGUgKyBcIlxcblwiICsgZGF0YS50b1N0cmluZygpKVxuXG4gICAgICAgICAgICB2ZXJib3NlICYmXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgICAgICBcIlNlcnZlZCBodG1sLWZpbGUgJ1wiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlcS5wYXRoICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiJyBmcm9tICdcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXREaXIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVxUGF0aCArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIidcIixcbiAgICAgICAgICAgICAgICApXG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICBcIkNhbnQgcmVhZCByZXF1ZXN0ZWQgaHRtbC1maWxlICdcIiArXG4gICAgICAgICAgICAgICAgICAgIHJlcS5wYXRoICtcbiAgICAgICAgICAgICAgICAgICAgXCInXCIgK1xuICAgICAgICAgICAgICAgICAgICBcIlxcbmZyb20gJ1wiICtcbiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0RGlyICtcbiAgICAgICAgICAgICAgICAgICAgcmVxUGF0aCArXG4gICAgICAgICAgICAgICAgICAgIFwiJzpcXG5cIixcbiAgICAgICAgICAgICAgICBlcnIsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICByZXMuc3RhdHVzKDUwMylcbiAgICAgICAgICAgIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3BsYWluXCIpXG4gICAgICAgICAgICByZXMuc2V0SGVhZGVyKFwiUmV0cnktQWZ0ZXJcIiwgXCI1XCIpXG4gICAgICAgICAgICByZXMuc2VuZChcbiAgICAgICAgICAgICAgICBcIkNhbnQgcmVhZCByZXF1ZXN0ZWQgaHRtbC1maWxlICdcIiArXG4gICAgICAgICAgICAgICAgICAgIHJlcS5wYXRoICtcbiAgICAgICAgICAgICAgICAgICAgXCInXCIsXG4gICAgICAgICAgICApXG4gICAgICAgIH1cbiAgICB9KVxuXG4gICAgYXV0b0V4dGVuc2lvblJlc29sdXRpb24gJiZcbiAgICAgICAgYXBwLnVzZShhc3luYyAocmVxLCByZXMsIG5leHQpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc29sdmVkRXh0ZW5zaW9uID1cbiAgICAgICAgICAgICAgICBhd2FpdCBhdXRvUmVzb2x2ZUV4dGVuc2lvbnMoXG4gICAgICAgICAgICAgICAgICAgIHJlcS51cmwsXG4gICAgICAgICAgICAgICAgICAgIHRhcmdldERpcixcbiAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSxcbiAgICAgICAgICAgICAgICApXG5cbiAgICAgICAgICAgIGlmIChyZXNvbHZlZEV4dGVuc2lvbikge1xuICAgICAgICAgICAgICAgIHJlcS51cmwgPSByZXNvbHZlZEV4dGVuc2lvblxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBuZXh0KClcbiAgICAgICAgfSlcblxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWModGFyZ2V0RGlyKSlcblxuICAgIHJldHVybiBhcHBcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGF1dG9SZXNvbHZlRXh0ZW5zaW9ucyhcbiAgICByZXFVcmw6IHN0cmluZyxcbiAgICB0YXJnZXREaXI6IHN0cmluZyxcbiAgICB2ZXJib3NlOiBib29sZWFuLFxuKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgICB3aGlsZSAocmVxVXJsLnN0YXJ0c1dpdGgoXCIvXCIpKSB7XG4gICAgICAgIHJlcVVybCA9IHJlcVVybC5zbGljZSgxKVxuICAgIH1cblxuICAgIGlmIChyZXFVcmwgPT09IFwiXCIpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICAgIGxldCByZWFsUGF0aCA9IHRhcmdldERpciArIFwiL1wiICsgcmVxVXJsXG4gICAgICAgIGlmICgoYXdhaXQgcGF0aFR5cGUocmVhbFBhdGgpKSA9PT0gXCJub25lXCIpIHtcbiAgICAgICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgYWZzLnJlYWRkaXIoXG4gICAgICAgICAgICAgICAgcGF0aC5kaXJuYW1lKHJlYWxQYXRoKSxcbiAgICAgICAgICAgIClcblxuICAgICAgICAgICAgY29uc3QgYmFzZU5hbWUgPSBwYXRoLmJhc2VuYW1lKHJlYWxQYXRoKVxuICAgICAgICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICAgICAgICAgICAgaWYgKGZpbGUuc3RhcnRzV2l0aChiYXNlTmFtZSArIFwiLlwiKSkge1xuICAgICAgICAgICAgICAgICAgICB2ZXJib3NlICYmXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8oXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCJBdXRvIHJlc29sdmVkIGV4dGVuc2lvbiBmb3IgJ1wiICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxVXJsICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXCInIHRvIGJlICdcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdGguZGlybmFtZShyZXFVcmwpICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIvXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXCInXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBwYXRoLmRpcm5hbWUocmVxVXJsKSArIFwiL1wiICsgZmlsZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICB2ZXJib3NlICYmXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAgIFwiRXJyb3Igd2hpbGUgYXV0byByZXNvbHZpbmcgZXh0ZW5zaW9uIGZvciBcIiArXG4gICAgICAgICAgICAgICAgICAgIHJlcVVybCArXG4gICAgICAgICAgICAgICAgICAgIFwiXFxuXCIsXG4gICAgICAgICAgICAgICAgZXJyLFxuICAgICAgICAgICAgKVxuICAgIH1cblxuICAgIHJldHVybiB1bmRlZmluZWRcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBhdGhUeXBlKFxuICAgIHBhdGg6IHN0cmluZyxcbik6IFByb21pc2U8XCJmaWxlXCIgfCBcImRpclwiIHwgXCJub25lXCI+IHtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCBzdGF0ID0gYXdhaXQgYWZzLnN0YXQocGF0aClcbiAgICAgICAgcmV0dXJuIHN0YXQuaXNGaWxlKCkgPyBcImZpbGVcIiA6IFwiZGlyXCJcbiAgICB9IGNhdGNoIHt9XG4gICAgcmV0dXJuIFwibm9uZVwiXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVXZWJTb2NrZXRTZXJ2ZXIoXG4gICAgaHR0cFNlcnZlcjogU2VydmVyPFxuICAgICAgICB0eXBlb2YgSW5jb21pbmdNZXNzYWdlLFxuICAgICAgICB0eXBlb2YgU2VydmVyUmVzcG9uc2VcbiAgICA+LFxuICAgIHJlZ2lzdGVyVHJpZ2dlcjogKHRyaWdnZXJIYW5kbGVyOiBUcmlnZ2VySGFuZGxlcikgPT4gdm9pZCxcbikge1xuICAgIGNvbnN0IHdzU2VydmVyID0gbmV3IFdlYlNvY2tldFNlcnZlcih7IG5vU2VydmVyOiB0cnVlIH0pXG4gICAgd3NTZXJ2ZXIub24oXCJjb25uZWN0aW9uXCIsICh3cywgcmVxKSA9PiB7XG4gICAgICAgIHJlZ2lzdGVyVHJpZ2dlcigoKSA9PiB7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgd3MucmVhZHlTdGF0ZSA9PT0gd3MuT1BFTiB8fFxuICAgICAgICAgICAgICAgIHdzLnJlYWR5U3RhdGUgPT09IHdzLkNPTk5FQ1RJTkdcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIHdzLmNsb3NlKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICB9KVxuXG4gICAgaHR0cFNlcnZlci5vbihcInVwZ3JhZGVcIiwgKHJlcSwgc29ja2V0LCBoZWFkKSA9PiB7XG4gICAgICAgIHdzU2VydmVyLmhhbmRsZVVwZ3JhZGUocmVxLCBzb2NrZXQsIGhlYWQsIHdzID0+IHtcbiAgICAgICAgICAgIHdzU2VydmVyLmVtaXQoXCJjb25uZWN0aW9uXCIsIHdzLCByZXEpXG4gICAgICAgIH0pXG4gICAgfSlcblxuICAgIHJldHVybiB3c1NlcnZlclxufVxuIl19