UNPKG

@firstfleet/auto-reload-brunch

Version:

Fork of brunch/auto-reload-brunch. Adds automatic browser reloading support to brunch.

193 lines (170 loc) 6.16 kB
"use strict"; import sysPath from "node:path"; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = sysPath.dirname(__filename); import fs from "node:fs"; import https from "node:https"; import {WebSocketServer} from 'ws'; import { isWorker } from "node:cluster"; import anymatch from "anymatch"; const startingPort = 9485; const portTryPool = 10; const fileName = "auto-reload.js"; export default class AutoReloader { constructor(config) { if (config == null) config = {}; if (config.autoReload) { throw new Error( "Warning: config.autoReload is no longer supported, please move it to config.plugins.autoReload" ); } const cfg = (this.config = (config.plugins && config.plugins.autoReload) || {}); if (cfg.port == null) { this.ports = []; for (var i = 0; i <= portTryPool; i++) this.ports.push(startingPort + i); } else { this.ports = Array.isArray(cfg.port) ? cfg.port.slice() : [cfg.port]; } if (cfg.host != null) { this.host = cfg.host; } if (config.persistent) { this.enabled = cfg.enabled == null ? true : cfg.enabled; } this.hot = config.hot; this.delay = cfg.delay; this.cssMatch = (cfg.match && cfg.match.stylesheets) || /\.css$/; this.jsMatch = (cfg.match && cfg.match.javascripts) || /\.js$/; this.forcewss = !!cfg.forcewss; this.connections = []; this.port = this.ports.shift(); if (cfg.keyPath && cfg.certPath) { this.key = fs.readFileSync(cfg.keyPath); this.cert = fs.readFileSync(cfg.certPath); this.ssl = !!(this.key && this.cert); } if (this.enabled && !isWorker) this.startServer(); } startServer() { const conns = this.connections; const cfg = this.config; const host = cfg.host || "0.0.0.0"; const port = this.port; if (this.ssl) { this.httpsServer = https.createServer({ key: this.key, cert: this.cert, }); this.httpsServer.listen(port, host); this.server = new WebSocketServer({ server: this.httpsServer }); } else { this.server = new WebSocketServer({ host, port }); } this.server.on("connection", (conn) => { conns.push(conn); conn.on("close", () => conns.splice(conns.indexOf(conn), 1)); }); this.server.on("error", (error) => { if (error.toString().match(/EADDRINUSE/)) { if (this.ports.length) { this.port = this.ports.shift(); this.startServer(); } else { error = `cannot start because port ${port} is in use`; } } }); } onCompile(changedFiles) { const enabled = this.enabled; const conns = this.connections; if (!enabled) return; const didCompile = changedFiles.length > 0; const isCss = (file) => anymatch(this.cssMatch, file.path); const isJs = (file) => anymatch(this.jsMatch, file.path); const isJsOrCss = (file) => isJs(file) || isCss(file); const allCss = didCompile && changedFiles.every(isCss); const allJs = this.hot && didCompile && changedFiles.every(isJs); const allJsOrCss = this.hot && didCompile && changedFiles.every(isJsOrCss); if (toString.call(enabled) === "[object Object]") { if (!(didCompile || enabled.assets)) return; if (allCss) { if (!enabled.css) return; } else if (didCompile) { const changedExts = changedFiles.map((x) => sysPath.extname(x.path).slice(1) ); const wasChanged = !Object.keys(enabled).some( (k) => enabled[k] && changedExts.indexOf(k) !== -1 ); if (wasChanged) return; } } const messages = []; if (allJs || allJsOrCss) messages.push("javascript"); if (allCss || allJsOrCss) messages.push("stylesheet"); if (messages.length === 0) messages.push("page"); const sendMessage = () => { conns .filter((connection) => connection.readyState === 1) .forEach((connection) => messages.forEach((message) => connection.send(message)) ); }; if (this.delay) { setTimeout(sendMessage, this.delay); } else { sendMessage(); } } get include() { console.log(sysPath.join(__dirname, "vendor", fileName)); return this.enabled ? [sysPath.join(__dirname, "vendor", fileName)] : []; } teardown() { if (this.server) this.server.close(); if (this.httpsServer) this.httpsServer.close(); } compile(file) { let finalData = file.data; if ( this.enabled && this.port !== startingPort && sysPath.basename(file.path) === fileName ) { finalData = finalData.replace(startingPort, this.port); } if ( this.enabled && (this.forcewss || this.ssl) && sysPath.basename(file.path) === fileName ) { finalData = finalData.replace("ws://", "wss://"); } if ( this.enabled && this.host && sysPath.basename(file.path) === fileName ) { finalData = finalData.replace( /var host = .*/, `var host = "${this.host}"` ); } return Promise.resolve(finalData); } } Object.setPrototypeOf(AutoReloader.prototype, { supportsHMR: true, brunchPlugin: true, type: "javascript", extension: "js", });