0
# Client Utilities
1
2
Browser-side utilities for error handling, webpack error formatting, and connection retry logic used by the React Refresh client-side runtime.
3
4
## Capabilities
5
6
### Error Event Handlers
7
8
Utilities for handling runtime errors and unhandled promise rejections in the browser.
9
10
```javascript { .api }
11
const { handleError, handleUnhandledRejection } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
12
13
/**
14
* Creates an error event handler for runtime errors
15
* @param {Function} errorReporter - Function to report errors to overlay
16
* @returns {Function} - Error event handler function
17
*/
18
function handleError(errorReporter: (error: Error) => void): (event: ErrorEvent) => void;
19
20
/**
21
* Creates an unhandled rejection handler for promise rejections
22
* @param {Function} errorReporter - Function to report errors to overlay
23
* @returns {Function} - Unhandled rejection event handler function
24
*/
25
function handleUnhandledRejection(errorReporter: (error: Error) => void): (event: PromiseRejectionEvent) => void;
26
```
27
28
**Usage Example:**
29
30
```javascript
31
const { handleError, handleUnhandledRejection } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
32
33
// Error reporting function
34
function reportError(error) {
35
console.error('Runtime error:', error);
36
// Send to error overlay
37
window.__react_refresh_error_overlay__.handleRuntimeError(error);
38
}
39
40
// Set up global error handlers
41
const errorHandler = handleError(reportError);
42
const rejectionHandler = handleUnhandledRejection(reportError);
43
44
// Install handlers
45
window.addEventListener('error', errorHandler);
46
window.addEventListener('unhandledrejection', rejectionHandler);
47
48
// Cleanup function
49
function removeErrorHandlers() {
50
window.removeEventListener('error', errorHandler);
51
window.removeEventListener('unhandledrejection', rejectionHandler);
52
}
53
```
54
55
### Format Webpack Errors
56
57
Formats webpack compilation errors for display in the error overlay with ANSI color support and stack trace processing.
58
59
```javascript { .api }
60
/**
61
* Formats webpack compilation errors for display
62
* @param {string[]} errors - Array of raw webpack error messages
63
* @returns {FormattedError[]} - Array of formatted error objects
64
*/
65
function formatWebpackErrors(errors: string[]): FormattedError[];
66
67
interface FormattedError {
68
message: string;
69
stack?: string;
70
file?: string;
71
lineNumber?: number;
72
columnNumber?: number;
73
}
74
```
75
76
**Usage Example:**
77
78
```javascript
79
const formatWebpackErrors = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/formatWebpackErrors');
80
81
// Raw webpack errors from compilation
82
const rawErrors = [
83
"Module not found: Error: Can't resolve './missing-component' in '/src'",
84
"SyntaxError: Unexpected token '}' (15:4)\n at Parser.pp$4.raise (/webpack/lib/Parser.js:349:13)"
85
];
86
87
// Format errors for display
88
const formattedErrors = formatWebpackErrors(rawErrors);
89
90
formattedErrors.forEach(error => {
91
console.log('Error message:', error.message);
92
console.log('File:', error.file);
93
console.log('Line:', error.lineNumber);
94
if (error.stack) {
95
console.log('Stack trace:', error.stack);
96
}
97
});
98
99
// Display in error overlay
100
formattedErrors.forEach(error => {
101
window.__react_refresh_error_overlay__.showCompileError(error.message);
102
});
103
```
104
105
### Connection Retry Logic
106
107
Retry utility for socket connections with exponential backoff and maximum retry limits.
108
109
```javascript { .api }
110
/**
111
* Runs a function with retry logic and exponential backoff
112
* @param {Function} fn - Function to execute with retry
113
* @param {number} maxAttempts - Maximum number of retry attempts
114
* @param {number} baseDelay - Base delay between retries in milliseconds
115
* @returns {Promise} - Promise that resolves when function succeeds or rejects after max attempts
116
*/
117
function runWithRetry<T>(
118
fn: () => Promise<T>,
119
maxAttempts?: number,
120
baseDelay?: number
121
): Promise<T>;
122
```
123
124
**Usage Example:**
125
126
```javascript
127
const runWithRetry = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/retry');
128
129
// Function that might fail (e.g., socket connection)
130
async function connectToWebSocket() {
131
return new Promise((resolve, reject) => {
132
const socket = new WebSocket('ws://localhost:8080');
133
134
socket.onopen = () => resolve(socket);
135
socket.onerror = (error) => reject(error);
136
137
// Timeout after 5 seconds
138
setTimeout(() => reject(new Error('Connection timeout')), 5000);
139
});
140
}
141
142
// Retry connection with exponential backoff
143
runWithRetry(connectToWebSocket, 5, 1000)
144
.then(socket => {
145
console.log('Connected successfully:', socket);
146
})
147
.catch(error => {
148
console.error('Failed to connect after retries:', error);
149
});
150
151
// Custom retry configuration
152
runWithRetry(
153
() => fetch('/api/health-check'),
154
3, // Max 3 attempts
155
500 // Start with 500ms delay
156
)
157
.then(response => response.json())
158
.then(data => console.log('Health check:', data))
159
.catch(error => console.error('Health check failed:', error));
160
```
161
162
## Advanced Usage Patterns
163
164
### Comprehensive Error Handling Setup
165
166
Set up complete error handling for a React application:
167
168
```javascript
169
const { handleError, handleUnhandledRejection } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
170
const formatWebpackErrors = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/formatWebpackErrors');
171
172
class ErrorManager {
173
constructor() {
174
this.errorOverlay = window.__react_refresh_error_overlay__;
175
this.setupErrorHandlers();
176
}
177
178
setupErrorHandlers() {
179
// Report runtime errors to overlay
180
const reportRuntimeError = (error) => {
181
if (this.errorOverlay) {
182
this.errorOverlay.handleRuntimeError(error);
183
}
184
// Also log to console for debugging
185
console.error('Runtime error:', error);
186
};
187
188
// Set up global error handlers
189
const errorHandler = handleError(reportRuntimeError);
190
const rejectionHandler = handleUnhandledRejection(reportRuntimeError);
191
192
window.addEventListener('error', errorHandler);
193
window.addEventListener('unhandledrejection', rejectionHandler);
194
195
// Store handlers for cleanup
196
this.errorHandler = errorHandler;
197
this.rejectionHandler = rejectionHandler;
198
}
199
200
handleCompilationErrors(errors) {
201
if (errors && errors.length > 0) {
202
const formatted = formatWebpackErrors(errors);
203
204
// Show first error in overlay
205
if (this.errorOverlay && formatted[0]) {
206
this.errorOverlay.showCompileError(formatted[0].message);
207
}
208
209
// Log all errors to console
210
formatted.forEach((error, index) => {
211
console.group(`Compilation Error ${index + 1}:`);
212
console.error(error.message);
213
if (error.file) console.log('File:', error.file);
214
if (error.lineNumber) console.log('Line:', error.lineNumber);
215
if (error.stack) console.log('Stack:', error.stack);
216
console.groupEnd();
217
});
218
}
219
}
220
221
clearErrors() {
222
if (this.errorOverlay) {
223
this.errorOverlay.clearRuntimeErrors();
224
this.errorOverlay.clearCompileError();
225
}
226
}
227
228
cleanup() {
229
if (this.errorHandler) {
230
window.removeEventListener('error', this.errorHandler);
231
}
232
if (this.rejectionHandler) {
233
window.removeEventListener('unhandledrejection', this.rejectionHandler);
234
}
235
}
236
}
237
238
// Initialize error management
239
const errorManager = new ErrorManager();
240
241
// Handle webpack hot updates
242
if (module.hot) {
243
module.hot.addStatusHandler(status => {
244
if (status === 'idle') {
245
errorManager.clearErrors();
246
}
247
});
248
}
249
```
250
251
### Resilient Socket Connection Manager
252
253
Create a robust socket connection with retry logic:
254
255
```javascript
256
const runWithRetry = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/retry');
257
const { handleError } = require('@pmmmwh/react-refresh-webpack-plugin/client/utils/errorEventHandlers');
258
259
class SocketManager {
260
constructor(url, options = {}) {
261
this.url = url;
262
this.maxRetries = options.maxRetries || 10;
263
this.baseDelay = options.baseDelay || 1000;
264
this.socket = null;
265
this.isConnecting = false;
266
this.messageHandlers = [];
267
268
// Set up error handling
269
this.errorHandler = handleError((error) => {
270
console.error('Socket error:', error);
271
this.handleConnectionError(error);
272
});
273
}
274
275
async connect() {
276
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
277
return this.socket;
278
}
279
280
if (this.isConnecting) {
281
return new Promise((resolve) => {
282
this.once('connected', resolve);
283
});
284
}
285
286
this.isConnecting = true;
287
288
try {
289
this.socket = await runWithRetry(
290
() => this.createConnection(),
291
this.maxRetries,
292
this.baseDelay
293
);
294
295
this.isConnecting = false;
296
this.emit('connected', this.socket);
297
return this.socket;
298
299
} catch (error) {
300
this.isConnecting = false;
301
throw error;
302
}
303
}
304
305
createConnection() {
306
return new Promise((resolve, reject) => {
307
const socket = new WebSocket(this.url);
308
309
socket.onopen = () => {
310
console.log('Socket connected');
311
resolve(socket);
312
};
313
314
socket.onmessage = (event) => {
315
this.handleMessage(JSON.parse(event.data));
316
};
317
318
socket.onclose = () => {
319
console.log('Socket disconnected');
320
this.handleDisconnection();
321
};
322
323
socket.onerror = (error) => {
324
console.error('Socket connection failed:', error);
325
reject(error);
326
};
327
328
// Connection timeout
329
setTimeout(() => {
330
if (socket.readyState === WebSocket.CONNECTING) {
331
socket.close();
332
reject(new Error('Connection timeout'));
333
}
334
}, 10000);
335
});
336
}
337
338
handleMessage(message) {
339
this.messageHandlers.forEach(handler => {
340
try {
341
handler(message);
342
} catch (error) {
343
this.errorHandler({ error });
344
}
345
});
346
}
347
348
handleConnectionError(error) {
349
// Attempt reconnection after delay
350
setTimeout(() => {
351
if (!this.socket || this.socket.readyState === WebSocket.CLOSED) {
352
this.connect().catch(retryError => {
353
console.error('Reconnection failed:', retryError);
354
});
355
}
356
}, this.baseDelay);
357
}
358
359
handleDisconnection() {
360
// Auto-reconnect after short delay
361
setTimeout(() => {
362
this.connect().catch(error => {
363
console.error('Auto-reconnect failed:', error);
364
});
365
}, 2000);
366
}
367
368
onMessage(handler) {
369
this.messageHandlers.push(handler);
370
}
371
372
// Simple event emitter methods
373
once(event, handler) {
374
const onceHandler = (...args) => {
375
handler(...args);
376
this.off(event, onceHandler);
377
};
378
this.on(event, onceHandler);
379
}
380
381
on(event, handler) {
382
if (!this.events) this.events = {};
383
if (!this.events[event]) this.events[event] = [];
384
this.events[event].push(handler);
385
}
386
387
off(event, handler) {
388
if (!this.events || !this.events[event]) return;
389
const index = this.events[event].indexOf(handler);
390
if (index > -1) this.events[event].splice(index, 1);
391
}
392
393
emit(event, ...args) {
394
if (!this.events || !this.events[event]) return;
395
this.events[event].forEach(handler => handler(...args));
396
}
397
}
398
399
// Usage
400
const socketManager = new SocketManager('ws://localhost:8080');
401
402
socketManager.onMessage((message) => {
403
console.log('Received message:', message);
404
405
// Handle different message types
406
switch (message.type) {
407
case 'errors':
408
errorManager.handleCompilationErrors(message.errors);
409
break;
410
case 'ok':
411
errorManager.clearErrors();
412
break;
413
}
414
});
415
416
// Connect with retry logic
417
socketManager.connect()
418
.then(() => console.log('Socket ready'))
419
.catch(error => console.error('Failed to establish connection:', error));
420
```