Relevant source files
The following files were used as context for generating this wiki page:
- packages/next/src/server/dev/hot-reloader-turbopack.ts
- packages/next/src/server/dev/hot-reloader-webpack.ts
- packages/next/src/client/dev/hot-reloader/app/web-socket.ts
- packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx
- packages/next/src/client/dev/hot-reloader/pages/websocket.ts
- packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts
- packages/next/src/server/dev/hot-middleware.ts
- packages/next/src/bundles/webpack/packages/JavascriptHotModuleReplacement.runtime.js
- packages/next/src/server/dev/on-demand-entry-handler.ts
- packages/next/src/client/dev/hot-reloader/turbopack-hot-reloader-common.ts
- packages/next/src/bundles/webpack/packages/lazy-compilation-web.js
- packages/next/src/client/page-bootstrap.ts
- packages/next/src/client/dev/hot-middleware-client.ts
- packages/next/src/bundles/webpack/packages/HotModuleReplacement.runtime.js
- packages/next/src/client/dev/error-overlay/websocket.ts
- packages/next/src/client/dev/noop-turbopack-hmr.ts
- packages/next/src/server/dev/hot-reloader-shared-utils.ts
- packages/next/src/server/dev/messages.ts
- packages/next/src/server/dev/parse-version-info.ts
- packages/next/src/shared/lib/format-webpack-messages.ts
- packages/next/src/shared/lib/invariant-error.ts
Overview
Hot Reloading powers the Next.js development experience by propagating code modifications, build status updates, and runtime errors instantly between the Development Server and the browser Client Runtime. It coordinates both Webpack and Turbopack compiler architectures, managing WebSocket transports, binary message serialization, and client reconnection lifecycles to deliver updates across both App Router and Pages Router applications.
Sources: packages/next/src/server/dev/hot-reloader-webpack.ts:454-490, packages/next/src/client/dev/hot-reloader/app/web-socket.ts:24-65, packages/next/src/client/dev/hot-reloader/pages/websocket.ts:28-65
Server Webpack Hot Reloader Architecture
Compiler Tracking and Middleware Initialization
WebpackHotMiddleware manages compilation states across the three Webpack compilers used in a Next.js development server: the client compiler, the server compiler, and the edge server compiler. During initialization, it attaches hooks to track compilation progress and errors for each compiler instance.
compilers[0].hooks.invalid.tap('webpack-hot-middleware', this.onClientInvalid)
compilers[0].hooks.done.tap('webpack-hot-middleware', this.onClientDone)
compilers[1].hooks.invalid.tap('webpack-hot-middleware', this.onServerInvalid)
compilers[1].hooks.done.tap('webpack-hot-middleware', this.onServerDone)
compilers[2].hooks.done.tap('webpack-hot-middleware', this.onEdgeServerDone)
compilers[2].hooks.invalid.tap('webpack-hot-middleware', this.onEdgeServerInvalid)Sources: packages/next/src/server/dev/hot-middleware.ts:112-127
When the client compiler enters an invalid state, onClientInvalid broadcasts a building status message to connected browsers, provided the server compiler does not have active compilation errors. When the server or edge server compilers finish with errors, their respective stats are cached in serverLatestStats or middlewareLatestStats, ensuring build errors are preserved and delivered via the developer overlay.
Sources: packages/next/src/server/dev/hot-middleware.ts:129-177
Subscriber Management and WebSocket Upgrades
The hot reloader exposes an onHMR method that upgrades incoming HTTP connections to persistent WebSocket sessions via wsServer.handleUpgrade. Client connections are tracked either by an associated htmlRequestId for App Router requests or added to clientsWithoutHtmlRequestId for legacy or Pages Router clients.
| Collection / Map | Key Type | Value Type | Purpose |
|---|---|---|---|
clientsWithoutHtmlRequestId | — | ws | Stores connected WebSocket clients that lack an explicit HTML request ID. |
clientsByHtmlRequestId | string | ws | Maps specific App Router HTML request identifiers to their active socket clients. |
runtimeErrorStates | string | RuntimeErrorStateMessage | Persists unhandled runtime error states per client when runtime error exposure is enabled. |
Sources: packages/next/src/server/dev/hot-middleware.ts:81-87, packages/next/src/server/dev/hot-middleware.ts:189-196
When a client establishes connection, the middleware synchronizes the current build state by evaluating client and server statistics.
function getStatsForSyncEvent(
clientStats: { ts: number; stats: webpack.Stats } | null,
serverStats: { ts: number; stats: webpack.Stats } | null
) {
if (!clientStats) return serverStats?.stats
if (!serverStats) return clientStats?.stats
if (serverStats.stats.hasErrors()) {
return serverStats.stats
}
return serverStats.ts > clientStats.ts ? serverStats.stats : clientStats.ts
}Sources: packages/next/src/server/dev/hot-middleware.ts:62-78
Warning
getStatsForSyncEvent explicitly prioritizes server compiler stats when they contain errors, preventing the client compilation stats from masking critical server-side build failures during initial synchronization.
Sources: packages/next/src/server/dev/hot-middleware.ts:69-74
Server Turbopack Hot Reloader Architecture
WebSocket Upgrades and Client Registration
Turbopack hot reloading handles live development client connections by instantiating a dedicated WebSocket server (wsServer) via ws.Server({ noServer: true }). When an incoming request triggers onHMR, the reloader upgrades the connection using wsServer.handleUpgrade.
onHMR(req, socket: Socket, head, onUpgrade) {
wsServer.handleUpgrade(req, socket, head, (client) => {
const clientIssues: EntryIssuesMap = new Map()
const subscriptions: Map<string, AsyncIterator<any>> = new Map()
const htmlRequestId = req.url
? new URL(req.url, 'http://n').searchParams.get('id')
: null
// Clients with a request ID are inferred App Router clients. If Cache
// Components is not enabled, we consider those legacy clients. Pages
// Router clients are also considered legacy clients. TODO: Maybe mark
// clients as App Router / Pages Router clients explicitly, instead of
// inferring it from the presence of a request ID.
if (htmlRequestId) {
clientsByHtmlRequestId.set(htmlRequestId, client)
const enableCacheComponents = nextConfig.cacheComponents
if (enableCacheComponents) {
onUpgrade(client, { isLegacyClient: false })
const cacheStatus = cacheStatusesByHtmlRequestId.get(htmlRequestId)
if (cacheStatus !== undefined) {
sendToClient(client, {
type: HMR_MESSAGE_SENT_TO_BROWSER.CACHE_INDICATOR,
state: cacheStatus,
})
cacheStatusesByHtmlRequestId.delete(htmlRequestId)
}
} else {
onUpgrade(client, { isLegacyClient: true })
}
connectReactDebugChannelForHtmlRequest(
htmlRequestId,
sendToClient.bind(null, client)
)
sendSerializedErrorsToClientForHtmlRequest(
htmlRequestId,
sendToClient.bind(null, client)
)
} else {
clientsWithoutHtmlRequestId.add(client)
onUpgrade(client, { isLegacyClient: true })
}Sources: packages/next/src/server/dev/hot-reloader-turbopack.ts:1539-1582
Clients are categorized based on the presence of an HTML request identifier parsed from the query parameters (id). App Router connections carry an htmlRequestId and are registered in clientsByHtmlRequestId, whereas connections without an identifier fall back to clientsWithoutHtmlRequestId as legacy clients.
| Client Type / Map | Identification Method | Target Collection | Upgrade Options |
|---|---|---|---|
| App Router Client | Presence of URL search parameter id | clientsByHtmlRequestId | { isLegacyClient: false } (if cacheComponents enabled) or true |
| Legacy / Pages Client | Absence of HTML request ID parameter | clientsWithoutHtmlRequestId | { isLegacyClient: true } |
Sources: packages/next/src/server/dev/hot-reloader-turbopack.ts:1544-1582
Note
When cacheComponents is active under the App Router, the server checks cacheStatusesByHtmlRequestId for any pre-existing cache status and immediately transmits a CACHE_INDICATOR message to the newly connected browser socket.
Sources: packages/next/src/server/dev/hot-reloader-turbopack.ts:1555-1565
Binary Framing and Message Serialization
Binary Message Packaging and Serialization
Next.js implements custom binary framing for specialized HMR messages sent from the server to the browser via WebSockets configured with binaryType = 'arraybuffer'. The server-side packaging function createBinaryHmrMessageData accepts a binary message payload and encodes it into a Uint8Array.
Message Type Constant (HMR_MESSAGE_SENT_TO_BROWSER) | Value / Structure | Serialization Layout |
|---|---|---|
ERRORS_TO_SHOW_IN_BROWSER | Binary error payload | [0: uint8 type][1...end: serializedErrors byte array] |
REACT_DEBUG_CHUNK | Request ID and chunk payload | [0: uint8 type][1: uint8 requestIdLength][2...2+len: encoded requestId][2+len...end: chunk bytes] |
Sources: packages/next/src/server/dev/messages.ts:12-58, packages/next/src/client/dev/hot-reloader/app/web-socket.ts:64
Caution
When serializing a REACT_DEBUG_CHUNK message, the requestId string length must not exceed 255 bytes; otherwise, an InvariantError is thrown because the single-byte length header cannot represent larger dimensions.
Sources: packages/next/src/server/dev/messages.ts:32-36
Client Parsing and Invariant Verification
On the client browser socket (packages/next/src/client/dev/hot-reloader/app/web-socket.ts), incoming MessageEvent data instances of type ArrayBuffer are intercepted and passed to parseBinaryMessage. This function utilizes DataView and TextDecoder to unpack the binary frames back into typed HMR message objects.
function parseBinaryMessage(data: ArrayBuffer): HmrMessageSentToBrowser {
assertByteLength(data, 1)
const view = new DataView(data)
const messageType = view.getUint8(0)
switch (messageType) {
case HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER: {
const serializedErrors = new Uint8Array(data, 1)
return {
type: HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER,
serializedErrors,
}
}
case HMR_MESSAGE_SENT_TO_BROWSER.REACT_DEBUG_CHUNK: {
assertByteLength(data, 2)
const requestIdLength = view.getUint8(1)
assertByteLength(data, 2 + requestIdLength)
const requestId = textDecoder.decode(
new Uint8Array(data, 2, requestIdLength)
)
const chunk =
data.byteLength > 2 + requestIdLength
? new Uint8Array(data, 2 + requestIdLength)
: null
return {
type: HMR_MESSAGE_SENT_TO_BROWSER.REACT_DEBUG_CHUNK,
requestId,
chunk,
}
}
default: {
throw new InvariantError(
`Invalid binary HMR message of type ${messageType}`
)
}
}
}Sources: packages/next/src/client/dev/hot-reloader/app/web-socket.ts:255-295
Warning
Every binary deserialization step invokes assertByteLength to verify that the incoming ArrayBuffer meets minimum length thresholds. If the buffer is truncated or smaller than expected, an InvariantError is thrown, indicating a malformed socket frame.
Sources: packages/next/src/client/dev/hot-reloader/app/web-socket.ts:270-272, packages/next/src/client/dev/hot-reloader/app/web-socket.ts:297-302
Client WebSocket Lifecycles and Reconnection
Transport Connection Setup and Initialization
Next.js manages development HMR socket connections across App Router and Pages Router through dedicated modules located under packages/next/src/client/dev/hot-reloader/. For App Router environments, createWebSocket requires self.__next_r to be defined as a document request ID, throwing an InvariantError if it is absent. The connection URL is constructed via getSocketUrl(assetPrefix) combined with the HMR path endpoint and the request identifier.
if (!self.__next_r) {
throw new InvariantError(
`Expected a request ID to be defined for the document via self.__next_r.`
)
}Sources: packages/next/src/client/dev/hot-reloader/app/web-socket.ts:28-32
Warning
Both App and Pages Router socket implementations enforce WEB_SOCKET_MAX_RECONNECTIONS. If continuous connection attempts exceed this threshold without a successful handshake, the client assumes the development server is no longer running and forces a full hard reload via window.location.reload().
Sources: packages/next/src/client/dev/hot-reloader/app/web-socket.ts:135-140, packages/next/src/client/dev/hot-reloader/pages/websocket.ts:77-82
Heartbeats, Visibility, and Online Handlers
App Router sockets maintain a periodic ping mechanism through useWebSocketPing, which evaluates active layouts via GlobalLayoutRouterContext. When enabled outside of Turbopack, it dispatches JSON ping payloads every 2.5 seconds over the open socket.
export function useWebSocketPing(webSocket: WebSocket | undefined) {
const { tree } = useContext(GlobalLayoutRouterContext)
useEffect(() => {
if (!webSocket) {
throw new InvariantError('Expected webSocket to be defined in dev mode.')
}
// Never send pings when using Turbopack as it's not used.
// Pings were originally used to keep track of active routes in on-demand-entries with webpack.
if (process.env.TURBOPACK) {
return
}
// Taken from on-demand-entries-client.js
const interval = setInterval(() => {
if (webSocket.readyState === webSocket.OPEN) {
webSocket.send(
JSON.stringify({
event: 'ping',
tree,
appDirRoute: true,
})
)
}
}, 2500)
return () => clearInterval(interval)
}, [tree, webSocket])
}Sources: packages/next/src/client/dev/hot-reloader/app/web-socket.ts:223-251
Tip
Both App and Pages Router socket clients listen for visibilitychange and online browser window events. When the document transitions to visible or network connectivity is restored while the WebSocket is not in an OPEN state, reconnection counters are reset and init() is immediately triggered to re-establish the transport channel.
Sources: packages/next/src/client/dev/hot-reloader/app/web-socket.ts:156-176, packages/next/src/client/dev/hot-reloader/pages/websocket.ts:98-118
App Router Update Dispatch and Diagnostics
Message Dispatch and Webpack Update Application
Incoming HMR messages for the App Router are handled by processMessage, which routes messages based on their type. For hot updates, handleHotUpdate branches on whether Turbopack is active: under Turbopack, it queries turbopackHmr.onBuilt(), reports HMR latency, and updates the dispatcher state, whereas under Webpack, it initiates tryApplyUpdatesWebpack.
export function processMessage(
message: HmrMessageSentToBrowser,
sendMessage: (message: string) => void,
processTurbopackMessage: (msg: TurbopackMessageSentToBrowser) => void,
staticIndicatorState: StaticIndicatorState
) {
function handleErrors(errors: ReadonlyArray<unknown>) {
// "Massage" webpack messages.
const formatted = formatWebpackMessages({
errors: errors,
warnings: [],
})
// Only show the first error.
dispatcher.onBuildError(formatted.errors[0])
// Also log them to the console.
for (let i = 0; i < formatted.errors.length; i++) {
console.error(stripAnsi(formatted.errors[i]))
}
// Do not attempt to reload now.
// We will reload on next success instead.
if (process.env.__NEXT_TEST_MODE) {
if (self.__NEXT_HMR_CB) {
self.__NEXT_HMR_CB(formatted.errors[0])
self.__NEXT_HMR_CB = null
}
}
}
function handleHotUpdate() {
if (process.env.TURBOPACK) {
const hmrUpdate = turbopackHmr!.onBuilt()
if (hmrUpdate != null) {
reportHmrLatency(
sendMessage,
[...hmrUpdate.updatedModules],
hmrUpdate.startMsSinceEpoch,
hmrUpdate.endMsSinceEpoch,
// suppress the `client-hmr-latency` event if the update was a no-op:
hmrUpdate.hasUpdates
)
}
dispatcher.onBuildOk()
// A successful build can be a no-op after recovering from an error, so
// there may be no server component change to resolve the test callback.
if (process.env.__NEXT_TEST_MODE && self.__NEXT_HMR_CB) {
self.__NEXT_HMR_CB()
self.__NEXT_HMR_CB = null
}
} else {
tryApplyUpdatesWebpack(sendMessage)
}
}Sources: packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:223-278
Note
tryApplyUpdatesWebpack checks isUpdateAvailable() and canApplyUpdates() before calling module.hot.check(false) followed by module.hot.apply(). If a new update arrives while an update is already in progress, tryApplyUpdatesWebpack recursively triggers itself to apply the latest compilation state.
Sources: packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:148-220
Error Formatting and Walkthrough
When build errors occur, processMessage delegates to handleErrors, which massages Webpack error payloads and logs them via stripAnsi and console.error. The execution path follows a explicit call chain from message handling through diagnostic formatting.
processMessagereceives the HMR browser message and invokeshandleErrorswhen error arrays are present.handleErrorspasses raw error collections intoformatWebpackMessages.formatWebpackMessagesmaps each error throughformatMessageto clean up Webpack-specific headers, loader paths, module traces, and syntax error labels.formatMessagereturns the normalized string whichdispatcher.onBuildErrorand the console logger display.
function handleErrors(errors: ReadonlyArray<unknown>) {
// "Massage" webpack messages.
const formatted = formatWebpackMessages({
errors: errors,
warnings: [],
})
// Only show the first error.
dispatcher.onBuildError(formatted.errors[0])
// Also log them to the console.
for (let i = 0; i < formatted.errors.length; i++) {
console.error(stripAnsi(formatted.errors[i]))
}
// Do not attempt to reload now.
// We will reload on next success instead.
if (process.env.__NEXT_TEST_MODE) {
if (self.__NEXT_HMR_CB) {
self.__NEXT_HMR_CB(formatted.errors[0])
self.__NEXT_HMR_CB = null
}
}
}Sources: packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:229-252, packages/next/src/shared/lib/format-webpack-messages.ts:217-256
Caution
formatWebpackMessages searches the formatted error array for ReactServerComponentsError. If found, it splices that specific error out and moves it to the front of the errors array (formattedErrors.unshift) so server component build failures take precedence in the overlay.
Sources: packages/next/src/shared/lib/format-webpack-messages.ts:228-244
Pages Router Update and Module Replacement
Hot Middleware Client Events and Page Lifecycle Routing
The Pages Router update client is initialized via hot-middleware-client.ts, which registers a custom HMR event subscriber to process server-driven page changes and reload triggers. When RELOAD_PAGE is received, the client sends a client-reload-page telemetry message over the websocket, sets its internal reloading flag, and invokes window.location.reload(). For REMOVED_PAGE and ADDED_PAGE messages, the client extracts the target page string and compares it against router.pathname and error page indicators (/404 or /_error).
export default () => {
const devClient = connect()
devClient.subscribeToHmrEvent((message) => {
if (reloading) return
// Retrieve the router if it's available
const router = window.next?.router
// Determine if we're on an error page or the router is not initialized
const isOnErrorPage =
!router || router.pathname === '/404' || router.pathname === '/_error'
switch (message.type) {
case HMR_MESSAGE_SENT_TO_BROWSER.RELOAD_PAGE: {
sendMessage(
JSON.stringify({
event: 'client-reload-page',
clientId: window.__nextDevClientId,
})
)
reloading = true
return window.location.reload()
}
case HMR_MESSAGE_SENT_TO_BROWSER.REMOVED_PAGE: {
const [page] = message.data
// Check if the removed page is the current page
const isCurrentPage = page === router?.pathname
// We enter here if the removed page is currently being viewed
// or if we happen to be on an error page.
if (isCurrentPage || isOnErrorPage) {
sendMessage(
JSON.stringify({
event: 'client-removed-page',
clientId: window.__nextDevClientId,
page,
})
)
return window.location.reload()
}
return
}
case HMR_MESSAGE_SENT_TO_BROWSER.ADDED_PAGE: {
const [page] = message.data
// Check if the added page is the current page
const isCurrentPage = page === router?.pathname
// Check if the page component is not yet loaded
const isPageNotLoaded =
page !== null && typeof router?.components?.[page] === 'undefined'
// We enter this block if the newly added page is the one currently being viewed
// but hasn't been loaded yet, or if we're on an error page.
if ((isCurrentPage && isPageNotLoaded) || isOnErrorPage) {
sendMessage(
JSON.stringify({
event: 'client-added-page',
clientId: window.__nextDevClientId,
page,
})
)
return window.location.reload()
}
return
}
case HMR_MESSAGE_SENT_TO_BROWSER.DEV_PAGES_MANIFEST_UPDATE: {
return
}
default: {
message satisfies never
}
}
})Sources: packages/next/src/client/dev/hot-middleware-client.ts:24-99
Warning
A page addition triggers a hard browser reload only if the added page matches the current route pathname and the corresponding component cache entry in router.components[page] is undefined, or if the view is currently on an error page.
Sources: packages/next/src/client/dev/hot-middleware-client.ts:68-91
Compilation State Handlers and Turbopack Integration
In hot-reloader-pages.ts, incoming messages are processed through handleSuccess, handleWarnings, and handleErrors. Successful compilations clear outdated console errors and check whether process.env.TURBOPACK is enabled; if so, it invokes turbopackHmr.onBuilt(), records HMR latency, and dispatches dispatcher.onBuildOk(). Under Webpack, it determines if an update is a hot update based on compilation history and router state before calling tryApplyUpdatesWebpack().
function handleSuccess() {
clearOutdatedErrors()
hasCompileErrors = false
if (process.env.TURBOPACK) {
const hmrUpdate = turbopackHmr!.onBuilt()
if (hmrUpdate != null) {
reportHmrLatency(
sendMessage,
[...hmrUpdate.updatedModules],
hmrUpdate.startMsSinceEpoch,
hmrUpdate.endMsSinceEpoch,
hmrUpdate.hasUpdates
)
}
dispatcher.onBuildOk()
} else {
const isHotUpdate =
!isFirstCompilation ||
(window.__NEXT_DATA__.page !== '/_error' && isUpdateAvailable())
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdatesWebpack()
}
}
isFirstCompilation = false
}Sources: packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:142-170, packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:274-307
Warnings invoke formatWebpackMessages with zero errors, limit console output to the first five warnings, strip ANSI escape codes, and subsequently attempt to apply updates if it is a hot update compilation. Errors suppress reloading entirely, formatting error arrays and forwarding the primary build error to dispatcher.onBuildError() and console.error.
function handleWarnings(warnings: any) {
clearOutdatedErrors()
const isHotUpdate = !isFirstCompilation
isFirstCompilation = false
hasCompileErrors = false
function printWarnings() {
// Print warnings to the console.
const formatted = formatWebpackMessages({
warnings: warnings,
errors: [],
})
if (typeof console !== 'undefined' && typeof console.warn === 'function') {
for (let i = 0; i < formatted.warnings?.length; i++) {
if (i === 5) {
console.warn(
'There were more warnings in other files.\n' +
'You can find a complete log in the terminal.'
)
break
}
console.warn(stripAnsi(formatted.warnings[i]))
}
}
}
printWarnings()
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
tryApplyUpdatesWebpack()
}
}
// Compilation with errors (e.g. syntax error or missing modules).
function handleErrors(errors: any) {
clearOutdatedErrors()
isFirstCompilation = false
hasCompileErrors = true
// "Massage" webpack messages.
var formatted = formatWebpackMessages({
errors: errors,
warnings: [],
})
// Only show the first error.
dispatcher.onBuildError(formatted.errors[0])
// Also log them to the console.
if (typeof console !== 'undefined' && typeof console.error === 'function') {
for (var i = 0; i < formatted.errors.length; i++) {
console.error(stripAnsi(formatted.errors[i]))
}
}
// Do not attempt to reload now.
// We will reload on next success instead.
if (process.env.__NEXT_TEST_MODE) {
if (self.__NEXT_HMR_CB) {
self.__NEXT_HMR_CB(formatted.errors[0])
self.__NEXT_HMR_CB = null
}
}
}Sources: packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:173-241
Module Invalidation and Replacement Runtime
Webpack HMR module replacement runtime manages state transitions across five discrete status values within the module system: idle, check, prepare, dispose, and apply. Modules support self-acceptance, dependency acceptance callbacks, error handlers, and explicit invalidation workflows.
| Status | Trigger / Condition | Action / Next State |
|---|---|---|
idle | Default state or post-apply cleanup | Allows check() execution; triggers manifest downloads |
check | hotCheck() invocation | Downloads update manifest; transitions to prepare or returns idle |
prepare | Manifest downloaded with updates | Tracks blocking promises; transitions to dispose via internalApply() |
dispose | Execution of module dispose handlers | Disables active modules, clears cache entries, strips parent/child links |
apply | Insertion of module factories | Executes new runtime modules, call accept handlers, loads self-accepted modules |
// status
var registeredStatusHandlers = [];
var currentStatus = "idle";
// while downloading
var blockingPromises = 0;
var blockingPromisesWaiting = [];
// The update info
var currentUpdateApplyHandlers;
var queuedInvalidatedModules;
$hmrModuleData$ = currentModuleData;
$interceptModuleExecution$.push(function (options) {
var module = options.module;
var require = createRequire(options.require, options.id);
module.hot = createModuleHotObject(options.id, module);
module.parents = currentParents;
module.children = [];
currentParents = [];
options.require = require;
});
$hmrDownloadUpdateHandlers$ = {};
$hmrInvalidateModuleHandlers$ = {};
function createRequire(require, moduleId) {
var me = installedModules[moduleId];
if (!me) return require;
var fn = function (request) {
if (me.hot.active) {
if (installedModules[request]) {
var parents = installedModules[request].parents;
if (parents.indexOf(moduleId) === -1) {
parents.push(moduleId);
}
} else {
currentParents = [moduleId];
currentChildModule = request;
}
if (me.children.indexOf(request) === -1) {
me.children.push(request);
}
} else {
console.warn(
"[HMR] unexpected require(" +
request +
") from disposed module " +
moduleId
);
currentParents = [];
}
return require(request);
};
var createPropertyDescriptor = function (name) {
return {
configurable: true,
enumerable: true,
get: function () {
return require[name];
},
set: function (value) {
require[name] = value;
}
};
};
for (var name in require) {
if (Object.prototype.hasOwnProperty.call(require, name) && name !== "e") {
Object.defineProperty(fn, name, createPropertyDescriptor(name));
}
}
fn.e = function (chunkId, fetchPriority) {
return trackBlockingPromise(require.e(chunkId, fetchPriority));
};
return fn;
}
function createModuleHotObject(moduleId, me) {
var _main = currentChildModule !== moduleId;Sources: packages/next/src/bundles/webpack/packages/HotModuleReplacement.runtime.js:27-188, packages/next/src/bundles/webpack/packages/HotModuleReplacement.runtime.js:259-390
Note
When a module calls invalidate(), _selfInvalidated is set to true. If called during prepare, check, dispose, or apply states, the module ID is pushed to queuedInvalidatedModules for execution during the subsequent apply pass.
Sources: packages/next/src/bundles/webpack/packages/HotModuleReplacement.runtime.js:155-188
Related
On This Page
OverviewServer Webpack Hot Reloader ArchitectureCompiler Tracking and Middleware InitializationSubscriber Management and WebSocket UpgradesServer Turbopack Hot Reloader ArchitectureWebSocket Upgrades and Client RegistrationBinary Framing and Message SerializationBinary Message Packaging and SerializationClient Parsing and Invariant VerificationClient WebSocket Lifecycles and ReconnectionTransport Connection Setup and InitializationHeartbeats, Visibility, and Online HandlersApp Router Update Dispatch and DiagnosticsMessage Dispatch and Webpack Update ApplicationError Formatting and WalkthroughPages Router Update and Module ReplacementHot Middleware Client Events and Page Lifecycle RoutingCompilation State Handlers and Turbopack IntegrationModule Invalidation and Replacement RuntimeRelated