Webpack plugin to enable React Fast Refresh (Hot Reloading) for React components during development
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Browser-side utilities for error handling, webpack error formatting, and connection retry logic used by the React Refresh client-side runtime.
Utilities for handling runtime errors and unhandled promise rejections in the browser.
const { handleError, handleUnhandledRejection } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
/**
* Creates an error event handler for runtime errors
* @param {Function} errorReporter - Function to report errors to overlay
* @returns {Function} - Error event handler function
*/
function handleError(errorReporter: (error: Error) => void): (event: ErrorEvent) => void;
/**
* Creates an unhandled rejection handler for promise rejections
* @param {Function} errorReporter - Function to report errors to overlay
* @returns {Function} - Unhandled rejection event handler function
*/
function handleUnhandledRejection(errorReporter: (error: Error) => void): (event: PromiseRejectionEvent) => void;Usage Example:
const { handleError, handleUnhandledRejection } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
// Error reporting function
function reportError(error) {
console.error('Runtime error:', error);
// Send to error overlay
window.__react_refresh_error_overlay__.handleRuntimeError(error);
}
// Set up global error handlers
const errorHandler = handleError(reportError);
const rejectionHandler = handleUnhandledRejection(reportError);
// Install handlers
window.addEventListener('error', errorHandler);
window.addEventListener('unhandledrejection', rejectionHandler);
// Cleanup function
function removeErrorHandlers() {
window.removeEventListener('error', errorHandler);
window.removeEventListener('unhandledrejection', rejectionHandler);
}Formats webpack compilation errors for display in the error overlay with ANSI color support and stack trace processing.
/**
* Formats webpack compilation errors for display
* @param {string[]} errors - Array of raw webpack error messages
* @returns {FormattedError[]} - Array of formatted error objects
*/
function formatWebpackErrors(errors: string[]): FormattedError[];
interface FormattedError {
message: string;
stack?: string;
file?: string;
lineNumber?: number;
columnNumber?: number;
}Usage Example:
const formatWebpackErrors = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/formatWebpackErrors');
// Raw webpack errors from compilation
const rawErrors = [
"Module not found: Error: Can't resolve './missing-component' in '/src'",
"SyntaxError: Unexpected token '}' (15:4)\n at Parser.pp$4.raise (/webpack/lib/Parser.js:349:13)"
];
// Format errors for display
const formattedErrors = formatWebpackErrors(rawErrors);
formattedErrors.forEach(error => {
console.log('Error message:', error.message);
console.log('File:', error.file);
console.log('Line:', error.lineNumber);
if (error.stack) {
console.log('Stack trace:', error.stack);
}
});
// Display in error overlay
formattedErrors.forEach(error => {
window.__react_refresh_error_overlay__.showCompileError(error.message);
});Retry utility for socket connections with exponential backoff and maximum retry limits.
/**
* Runs a function with retry logic and exponential backoff
* @param {Function} fn - Function to execute with retry
* @param {number} maxAttempts - Maximum number of retry attempts
* @param {number} baseDelay - Base delay between retries in milliseconds
* @returns {Promise} - Promise that resolves when function succeeds or rejects after max attempts
*/
function runWithRetry<T>(
fn: () => Promise<T>,
maxAttempts?: number,
baseDelay?: number
): Promise<T>;Usage Example:
const runWithRetry = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/retry');
// Function that might fail (e.g., socket connection)
async function connectToWebSocket() {
return new Promise((resolve, reject) => {
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = () => resolve(socket);
socket.onerror = (error) => reject(error);
// Timeout after 5 seconds
setTimeout(() => reject(new Error('Connection timeout')), 5000);
});
}
// Retry connection with exponential backoff
runWithRetry(connectToWebSocket, 5, 1000)
.then(socket => {
console.log('Connected successfully:', socket);
})
.catch(error => {
console.error('Failed to connect after retries:', error);
});
// Custom retry configuration
runWithRetry(
() => fetch('/api/health-check'),
3, // Max 3 attempts
500 // Start with 500ms delay
)
.then(response => response.json())
.then(data => console.log('Health check:', data))
.catch(error => console.error('Health check failed:', error));Set up complete error handling for a React application:
const { handleError, handleUnhandledRejection } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
const formatWebpackErrors = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/formatWebpackErrors');
class ErrorManager {
constructor() {
this.errorOverlay = window.__react_refresh_error_overlay__;
this.setupErrorHandlers();
}
setupErrorHandlers() {
// Report runtime errors to overlay
const reportRuntimeError = (error) => {
if (this.errorOverlay) {
this.errorOverlay.handleRuntimeError(error);
}
// Also log to console for debugging
console.error('Runtime error:', error);
};
// Set up global error handlers
const errorHandler = handleError(reportRuntimeError);
const rejectionHandler = handleUnhandledRejection(reportRuntimeError);
window.addEventListener('error', errorHandler);
window.addEventListener('unhandledrejection', rejectionHandler);
// Store handlers for cleanup
this.errorHandler = errorHandler;
this.rejectionHandler = rejectionHandler;
}
handleCompilationErrors(errors) {
if (errors && errors.length > 0) {
const formatted = formatWebpackErrors(errors);
// Show first error in overlay
if (this.errorOverlay && formatted[0]) {
this.errorOverlay.showCompileError(formatted[0].message);
}
// Log all errors to console
formatted.forEach((error, index) => {
console.group(`Compilation Error ${index + 1}:`);
console.error(error.message);
if (error.file) console.log('File:', error.file);
if (error.lineNumber) console.log('Line:', error.lineNumber);
if (error.stack) console.log('Stack:', error.stack);
console.groupEnd();
});
}
}
clearErrors() {
if (this.errorOverlay) {
this.errorOverlay.clearRuntimeErrors();
this.errorOverlay.clearCompileError();
}
}
cleanup() {
if (this.errorHandler) {
window.removeEventListener('error', this.errorHandler);
}
if (this.rejectionHandler) {
window.removeEventListener('unhandledrejection', this.rejectionHandler);
}
}
}
// Initialize error management
const errorManager = new ErrorManager();
// Handle webpack hot updates
if (module.hot) {
module.hot.addStatusHandler(status => {
if (status === 'idle') {
errorManager.clearErrors();
}
});
}Create a robust socket connection with retry logic:
const runWithRetry = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/retry');
const { handleError } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
class SocketManager {
constructor(url, options = {}) {
this.url = url;
this.maxRetries = options.maxRetries || 10;
this.baseDelay = options.baseDelay || 1000;
this.socket = null;
this.isConnecting = false;
this.messageHandlers = [];
// Set up error handling
this.errorHandler = handleError((error) => {
console.error('Socket error:', error);
this.handleConnectionError(error);
});
}
async connect() {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
return this.socket;
}
if (this.isConnecting) {
return new Promise((resolve) => {
this.once('connected', resolve);
});
}
this.isConnecting = true;
try {
this.socket = await runWithRetry(
() => this.createConnection(),
this.maxRetries,
this.baseDelay
);
this.isConnecting = false;
this.emit('connected', this.socket);
return this.socket;
} catch (error) {
this.isConnecting = false;
throw error;
}
}
createConnection() {
return new Promise((resolve, reject) => {
const socket = new WebSocket(this.url);
socket.onopen = () => {
console.log('Socket connected');
resolve(socket);
};
socket.onmessage = (event) => {
this.handleMessage(JSON.parse(event.data));
};
socket.onclose = () => {
console.log('Socket disconnected');
this.handleDisconnection();
};
socket.onerror = (error) => {
console.error('Socket connection failed:', error);
reject(error);
};
// Connection timeout
setTimeout(() => {
if (socket.readyState === WebSocket.CONNECTING) {
socket.close();
reject(new Error('Connection timeout'));
}
}, 10000);
});
}
handleMessage(message) {
this.messageHandlers.forEach(handler => {
try {
handler(message);
} catch (error) {
this.errorHandler({ error });
}
});
}
handleConnectionError(error) {
// Attempt reconnection after delay
setTimeout(() => {
if (!this.socket || this.socket.readyState === WebSocket.CLOSED) {
this.connect().catch(retryError => {
console.error('Reconnection failed:', retryError);
});
}
}, this.baseDelay);
}
handleDisconnection() {
// Auto-reconnect after short delay
setTimeout(() => {
this.connect().catch(error => {
console.error('Auto-reconnect failed:', error);
});
}, 2000);
}
onMessage(handler) {
this.messageHandlers.push(handler);
}
// Simple event emitter methods
once(event, handler) {
const onceHandler = (...args) => {
handler(...args);
this.off(event, onceHandler);
};
this.on(event, onceHandler);
}
on(event, handler) {
if (!this.events) this.events = {};
if (!this.events[event]) this.events[event] = [];
this.events[event].push(handler);
}
off(event, handler) {
if (!this.events || !this.events[event]) return;
const index = this.events[event].indexOf(handler);
if (index > -1) this.events[event].splice(index, 1);
}
emit(event, ...args) {
if (!this.events || !this.events[event]) return;
this.events[event].forEach(handler => handler(...args));
}
}
// Usage
const socketManager = new SocketManager('ws://localhost:8080');
socketManager.onMessage((message) => {
console.log('Received message:', message);
// Handle different message types
switch (message.type) {
case 'errors':
errorManager.handleCompilationErrors(message.errors);
break;
case 'ok':
errorManager.clearErrors();
break;
}
});
// Connect with retry logic
socketManager.connect()
.then(() => console.log('Socket ready'))
.catch(error => console.error('Failed to establish connection:', error));Install with Tessl CLI
npx tessl i tessl/npm-pmmmwh--react-refresh-webpack-plugin