import { Server } from "http"; import url from "url"; import { Ctx, useCtx } from "./ctx"; import { copy, emptyDir } from "fs-extra"; import path from "path"; const integrateClientJs = async function (ctx: Ctx) { const publicFolder = "sosseDevSocketClient"; if (!globalThis.sosseDevClientCopied) { const clientJsPath = path.resolve( __dirname, "..", "dev-socket-client", "dist" ); const publicPath = path.resolve(ctx.publicDir, publicFolder); await emptyDir(publicPath); await copy( path.resolve(clientJsPath, "main.umd.js"), path.resolve(publicPath, "main.umd.js") ); await copy( path.resolve(clientJsPath, "main.umd.js.map"), path.resolve(publicPath, "main.umd.js.map") ); globalThis.sosseDevClientCopied = true; } const publicUrl = `/${publicFolder}/main.umd.js`; ctx.assets.sosseDev = { url: publicUrl, html: ` `, }; ctx.injectHtml.head.sosseDev = ctx.assets.sosseDev.html; }; export const devSocket = async function ({ server, enable = process.env.NODE_ENV !== "production", }: { server: Server; enable?: boolean; wait?: number; }) { if (!enable) { return; } const WebSocket = require("ws"); const ctx = useCtx(); await integrateClientJs(ctx); const wss = new WebSocket.Server({ noServer: true, }); server.on("upgrade", function upgrade(request, socket, head) { const pathname = url.parse(request.url).pathname; if (pathname === "/sosse-dev") { wss.handleUpgrade(request, socket, head, function done(ws) { wss.emit("connection", ws, request); if (ctx.errors.length) { sendError(ws); } }); } else { socket.destroy(); } }); const restartListener = function () { ctx.errors = []; wss.close(); ctx.events.removeListener("restart", restartListener); ctx.events.removeListener("error", errorListener); ctx.events.removeListener("reload", reloadListener); }; const reloadListener = function () { for (const client of wss.clients) { client.send(JSON.stringify({ type: "reload" })); } ctx.errors = []; }; const sendError = function (client) { client.send( JSON.stringify({ type: "error", errors: ctx.errors, }) ); }; const errorListener = function () { for (const client of wss.clients) { sendError(client); } }; const startedListener = function () { ctx.events.removeListener("started", startedListener); ctx.events.on("reload", reloadListener); ctx.events.on("restart", restartListener); ctx.events.on("error", errorListener); }; ctx.events.on("started", startedListener); };