Module required for evaluating Metro bundles with async loading and HMR support.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
WebSocket-based client for receiving and applying hot updates during development. The HMR client provides real-time code updates without losing application state, enabling fast development iteration cycles.
Main class for establishing and managing HMR WebSocket connections with the Metro development server.
/**
* WebSocket-based Hot Module Replacement client
* Extends EventEmitter for event-driven update handling
*/
class HMRClient extends EventEmitter {
/**
* Creates a new HMR client connection
* @param url - WebSocket URL for the Metro HMR server
*/
constructor(url: string);
/**
* Closes the WebSocket connection
*/
close(): void;
/**
* Sends a message to the HMR server
* @param message - String message to send
*/
send(message: string): void;
/**
* Enables HMR and applies any pending updates
*/
enable(): void;
/**
* Disables HMR (updates will be queued but not applied)
*/
disable(): void;
/**
* Returns whether HMR is currently enabled
* @returns True if HMR is enabled
*/
isEnabled(): boolean;
/**
* Returns whether there are updates waiting to be applied
* @returns True if there are pending updates
*/
hasPendingUpdates(): boolean;
}
type SocketState = 'opening' | 'open' | 'closed';Usage Examples:
const HMRClient = require("metro-runtime/modules/HMRClient");
// Basic HMR client setup
const client = new HMRClient("ws://localhost:8081/hot");
// Enable HMR when ready
client.enable();
// Set up event listeners
client.on("open", () => {
console.log("HMR connection established");
});
client.on("update", (update) => {
console.log("Received hot update:", update.revisionId);
console.log("Modified modules:", update.modified.length);
});
client.on("error", (error) => {
console.error("HMR error:", error);
});
// Disable HMR temporarily
const pauseHMR = () => {
client.disable();
console.log("HMR paused, updates will be queued");
};
// Re-enable and apply queued updates
const resumeHMR = () => {
client.enable();
console.log("HMR resumed, applying queued updates");
};The HMRClient emits various events for different stages of the update process:
// Connection events
client.on('open', () => void);
client.on('close', (closeEvent) => void);
client.on('connection-error', (error) => void);
// HMR lifecycle events
client.on('bundle-registered', () => void);
client.on('update-start', (data: {isInitialUpdate: boolean}) => void);
client.on('update', (update: HmrUpdate) => void);
client.on('update-done', () => void);
client.on('error', (error: FormattedError) => void);Event Usage Examples:
// Complete HMR event handling
const setupHMRClient = (url) => {
const client = new HMRClient(url);
// Connection lifecycle
client.on("open", () => {
console.log("🔥 HMR connected");
client.send(JSON.stringify({
type: "register-entrypoints",
entryPoints: ["index.js"]
}));
});
client.on("close", (event) => {
console.log("❌ HMR disconnected:", event.reason);
});
client.on("connection-error", (error) => {
console.error("🚨 HMR connection failed:", error);
});
// Update lifecycle
client.on("bundle-registered", () => {
console.log("📦 Bundle registered with HMR server");
});
client.on("update-start", ({ isInitialUpdate }) => {
if (isInitialUpdate) {
console.log("🎬 Initial HMR update");
} else {
console.log("⚡ Hot update starting");
}
});
client.on("update", (update) => {
const { added, modified, deleted, revisionId } = update;
console.log(`🔄 Update ${revisionId}:`, {
added: added.length,
modified: modified.length,
deleted: deleted.length
});
});
client.on("update-done", () => {
console.log("✅ Hot update completed");
});
client.on("error", (error) => {
console.error("💥 HMR error:", error.message);
if (error.errors) {
error.errors.forEach(err => console.error(" -", err.description));
}
});
return client;
};The client handles various message types from the Metro server:
type HmrUpdate = {
+added: $ReadOnlyArray<HmrModule>,
+deleted: $ReadOnlyArray<number>,
+isInitialUpdate: boolean,
+modified: $ReadOnlyArray<HmrModule>,
+revisionId: string,
};
type HmrModule = {
+module: [number, string], // [moduleId, moduleCode]
+sourceMappingURL: string,
+sourceURL: string,
};Complete message types received from the Metro HMR server:
type HmrMessage =
| {+type: 'bundle-registered'}
| {+type: 'update-start', +body: {+isInitialUpdate: boolean}}
| {+type: 'update-done'}
| {+type: 'update', +body: HmrUpdate}
| {+type: 'error', +body: FormattedError};
type FormattedError = {
+type: string,
+message: string,
+errors: Array<{description: string, ...}>,
};Messages that can be sent to the server:
type HmrClientMessage =
| {
+type: 'register-entrypoints',
+entryPoints: Array<string>,
}
| {
+type: 'log',
+level: 'trace' | 'info' | 'warn' | 'log' | 'group' | 'groupCollapsed' | 'groupEnd' | 'debug',
+data: Array<mixed>,
+mode: 'BRIDGE' | 'NOBRIDGE',
}
| {
+type: 'log-opt-in',
};The HMR client automatically processes updates through code injection:
// Updates are processed automatically when HMR is enabled
// The client injects new module code using eval() or globalEvalWithSourceUrl()
// Manual update handling (advanced usage)
client.on("update", (update) => {
// Updates are automatically applied when HMR is enabled
// This event is for monitoring/logging purposes
update.added.forEach(module => {
const [moduleId, code] = module.module;
console.log(`Added module ${moduleId}`);
});
update.modified.forEach(module => {
const [moduleId, code] = module.module;
console.log(`Modified module ${moduleId}`);
});
update.deleted.forEach(moduleId => {
console.log(`Deleted module ${moduleId}`);
});
});The client handles connection states and message queuing:
// Connection state management
const monitorConnection = (client) => {
const checkState = () => {
console.log("HMR State:", {
enabled: client.isEnabled(),
hasPending: client.hasPendingUpdates()
});
};
setInterval(checkState, 5000);
};
// Graceful shutdown
const shutdownHMR = (client) => {
console.log("Shutting down HMR client...");
client.disable();
client.close();
};
// Reconnection logic (manual implementation)
const createReconnectingClient = (url) => {
let client = new HMRClient(url);
client.on("close", () => {
console.log("Attempting to reconnect in 5 seconds...");
setTimeout(() => {
client = new HMRClient(url);
client.enable();
}, 5000);
});
return client;
};The HMR client integrates seamlessly with React Fast Refresh for component-level updates:
// React Fast Refresh integration is handled automatically
// The client works with Metro's React Refresh implementation
// Custom refresh boundary detection (advanced)
client.on("update", (update) => {
const hasReactComponents = update.modified.some(module => {
const [, code] = module.module;
return code.includes("$RefreshReg$") || code.includes("react");
});
if (hasReactComponents) {
console.log("🔄 React components updated with Fast Refresh");
}
});