Skip to content

Instantly share code, notes, and snippets.

@shripadk
Created May 3, 2025 13:57
Show Gist options
  • Save shripadk/f1b28cb92b00b7ac45625d211fa5f7c8 to your computer and use it in GitHub Desktop.
Save shripadk/f1b28cb92b00b7ac45625d211fa5f7c8 to your computer and use it in GitHub Desktop.
let host = window.location.hostname;
// --- Exponential Backoff Configuration ---
const baseRetryDelay = 1000; // Initial delay: 1 second
const maxRetryDelay = 30000; // Maximum delay: 30 seconds
let currentRetryDelay = baseRetryDelay;
let reconnectTimerId = null; // To hold the setTimeout ID
function connect() {
// Clear any pending reconnect timer if connect is called manually or rapidly
if (reconnectTimerId) {
clearTimeout(reconnectTimerId);
reconnectTimerId = null;
}
// Ensure protocol and reload_port are defined before attempting connection
if (typeof protocol === 'undefined' || typeof reload_port === 'undefined') {
console.error("Live Reload Error: 'protocol' or 'reload_port' is not defined.");
// Optional: Implement a fallback or stop trying if config is missing
// For now, we'll log the error and let the backoff handle retries,
// though they will likely fail until config is fixed.
}
let wsUrl = `${protocol}${host}:${reload_port}/live_reload`;
console.log(`Attempting to connect to ${wsUrl}...`);
let ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log('Hot-reload connection established.');
// Reset retry delay on successful connection
currentRetryDelay = baseRetryDelay;
};
ws.onmessage = (ev) => {
try {
let msg = JSON.parse(ev.data);
if (msg.css) {
let found = false;
document.querySelectorAll("link").forEach((link) => {
if (link.getAttribute('href').includes(msg.css)) {
let newHref = '/' + msg.css + '?version=' + new Date().getMilliseconds();
link.setAttribute('href', newHref);
found = true;
}
});
if (!found) console.warn(`CSS hot-reload: Could not find a <link href=/\"${msg.css}\"> element`);
}
if(msg.view) {
patch(msg.view);
if (msg.view.search("SetAttribute") !== -1 && msg.view.search("class") !== -1) {
// Tailwind typically takes few milliseconds to less than a second
// to generate output styles. So waiting 1 second before replacing
// stylesheet seems fine for now. But need a more robust solution
// later. Perhaps track mtime of CSS file and use that for notification?
setTimeout(() => {
let link = document.querySelector("#leptos");
let href = link.href;
link.remove();
link = document.createElement("link");
link.setAttribute("id", "leptos");
link.setAttribute("rel", "stylesheet");
link.href = href;
document.head.appendChild(link);
}, 1000);
}
}
} catch (e) {
console.error("Error processing live-reload message:", e);
}
};
ws.onerror = (error) => {
// Log WebSocket errors (e.g., connection refused)
console.error('WebSocket Error:', error);
// Note: 'onerror' is usually followed by 'onclose', so backoff logic is handled there.
};
ws.onclose = (event) => {
// event.code provides reason for closure (e.g., 1006 for abnormal closure)
console.warn(`Live-reload connection closed (Code: ${event.code}). Reconnecting in ${currentRetryDelay / 1000} seconds...`);
// Schedule the next connection attempt with the current delay
reconnectTimerId = setTimeout(connect, currentRetryDelay);
// Calculate the next delay for the *subsequent* attempt (exponential backoff)
currentRetryDelay = Math.min(currentRetryDelay * 2, maxRetryDelay);
};
}
// --- Initial Connection Attempt ---
// Make sure 'protocol' and 'reload_port' are defined before the first call
if (typeof protocol !== 'undefined' && typeof reload_port !== 'undefined') {
connect();
} else {
console.error("Live Reload disabled: 'protocol' or 'reload_port' is not defined globally.");
// Optionally, you could try to define defaults here if appropriate
// protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
// reload_port = 3001; // Example default
// connect();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment