new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Vite · Guide · all subjects

environment api/hmr

47 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Server entry HMR setup

Add import.meta.hot.accept() in your server entry file for optimal HMR behavior. Without this, server file changes will invalidate the entire server module graph.

hotUpdate hook type and signature

The hotUpdate hook type is: (this: { environment: DevEnvironment }, options: HotUpdateOptions) => Array<EnvironmentModuleNode> | void | Promise<Array<EnvironmentModuleNode> | void>. Kind: async, sequential. Scope: per-environment. It receives a context object with: type ('create' | 'update' | 'delete'), file (string), timestamp (number), modules (Array<EnvironmentModuleNode>), read (() => string | Promise<string>), and server (ViteDevServer). this.environment is the module execution environment where the file update is being processed.

hotUpdate hook modules parameter

The modules parameter in the hotUpdate hook is an array of modules in the current environment that are affected by the changed file. It is an array because a single file may map to multiple served modules, such as Vue SFCs.

hotUpdate hook read function

The read function in the hotUpdate hook is an async function that returns the content of the file as a string or promise. It is provided because on some systems the file change callback may fire too fast before the editor finishes updating the file, and direct fs.readFile would return empty content. The read function normalizes this behavior.

hotUpdate hook strategies for HMR handling

The hotUpdate hook can: 1) Filter and narrow down the affected module list for more accurate HMR, 2) Return an empty array and perform a full reload by invalidating modules manually and sending a full-reload message via this.environment.hot.send({ type: 'full-reload' }), 3) Return an empty array and perform custom HMR handling by sending custom events to the client via this.environment.hot.send() with a custom type and event name, with client code registering handlers using the HMR API with import.meta.hot.on().

environment.hot for plugin-application communication

environment.hot allows plugins to communicate with code on the application side for a given environment. It is the equivalent of the Client-server Communication feature but supports environments other than the client environment. This feature is only available for environments that support HMR.

Multiple application instances in same environment

There can be multiple application instances running in the same environment. For example, multiple browser tabs are separate application instances with separate server connections. When a new connection is established, a vite:client:connect event is emitted on the environment's hot instance. When a connection closes, a vite:client:disconnect event is emitted. Each event handler receives the NormalizedHotChannelClient with a send method to message that specific instance.

NormalizedHotChannelClient for sending to specific instances

The NormalizedHotChannelClient is passed as the second argument to event handlers for vite:client:connect and vite:client:disconnect events. It has a send method to send messages to that specific application instance. The client reference is always the same for the same connection, allowing you to track connections.

Broadcasting messages via environment.hot.send

Calling server.environments[name].hot.send(eventName, data) broadcasts a message to all application instances in that environment. Individual instances can also be targeted by using the client parameter passed to event handlers.

Client-side HMR API usage with environment messages

Client code can register handlers for custom environment messages using import.meta.hot.on(eventName, handler). This works with messages sent from plugins via environment.hot.send(). The same API works for both standard HMR events and custom plugin events.

Example full reload in hotUpdate hook

Example of performing a full reload in the hotUpdate hook: ```js hotUpdate({ modules, timestamp }) { if (this.environment.name !== 'client') return // Invalidate modules manually const invalidatedModules = new Set() for (const mod of modules) { this.environment.moduleGraph.invalidateModule( mod, invalidatedModules, timestamp, true ) } this.environment.hot.send({ type: 'full-reload' }) return [] } ``` This invalidates modules and sends a full-reload message to trigger a page reload.

Example custom HMR handling in hotUpdate hook

Example of custom HMR handling in the hotUpdate hook: ```js hotUpdate() { if (this.environment.name !== 'client') return this.environment.hot.send({ type: 'custom', event: 'special-update', data: {} }) return [] } ``` Client code registers the handler with: ```js if (import.meta.hot) { import.meta.hot.on('special-update', (data) => { // perform custom update }) } ```

Example plugin-application communication

Example of plugin-side communication in configureServer: ```js configureServer(server) { server.environments.ssr.hot.on('my:greetings', (data, client) => { // do something with the data, // and optionally send a response to that application instance client.send('my:foo:reply', `Hello from server! You said: ${data}`) }) // broadcast a message to all application instances server.environments.ssr.hot.send('my:foo', 'Hello from server!') } ``` Client-side uses import.meta.hot to send and receive messages the same way as with Client-server Communication.

HotChannel skipFsCheck property

By default, HotChannel transports have server.fs restrictions applied, meaning only files within allowed directories can be served. If your transport is not exposed over the network (e.g., communicates via worker threads or in-process calls), you can set skipFsCheck: true on the HotChannel to bypass these restrictions.

HotChannel connect and disconnect events

When HotChannel has on and off methods, you must implement vite:client:connect and vite:client:disconnect events. The vite:client:connect event should be emitted when connection is established. The vite:client:disconnect event should be emitted when connection is closed. The HotChannelClient object passed to the event handler must have the same reference for the same connection.

ModuleRunnerTransport invoke method without connect

If ModuleRunnerTransport only implements invoke method without connect, HMR must be disabled by setting hmr: false. The invoke method can use HTTP requests or other communication mechanisms that don't require an open connection.

DevEnvironment.hot.handleInvoke for custom transports

The handleInvoke method in the NormalizedHotChannel can be used to process invoke requests from custom transports. This is useful when using transport mechanisms like HTTP requests where you need to handle the invoke payload on the server and return the result.

ViteHotContext interface definition

ViteHotContext is the main HMR API interface exposed via import.meta.hot. It provides the following properties and methods: readonly data (any), accept() with multiple overloads for self-accepting or accepting dependencies, dispose(cb) for cleanup, prune(cb) for cleanup when module is removed, invalidate(message) to propagate updates to importers, on(event, cb) to listen to HMR events, off(event, cb) to remove event listeners, and send(event, data) to send custom events to the dev server.

HMR API access via import.meta.hot

Vite exposes its manual HMR API via the special import.meta.hot object which is of type ViteHotContext. The hot property on ImportMeta is readonly and optional.

Guard HMR code with conditional check

All HMR API usage must be guarded with a conditional block checking if (import.meta.hot) so that the code can be tree-shaken in production.

Add vite/client types to tsconfig.json

To get TypeScript IntelliSense for import.meta.hot, add 'vite/client' to the types array in tsconfig.json compilerOptions. Vite provides type definitions for import.meta.hot in vite/client.d.ts.

hot.accept(cb) for self-accepting modules

A module can self-accept hot updates by calling import.meta.hot.accept with a callback that receives the updated module as ModuleNamespace or undefined (if a SyntaxError occurred). A module that accepts hot updates is considered an HMR boundary. The call to hot.accept must appear as the literal text 'import.meta.hot.accept(' (whitespace-sensitive) in the source code for static analysis to enable HMR support.

hot.accept(dep, cb) accepting single dependency

A module can accept updates from a direct dependency without reloading itself by calling import.meta.hot.accept(depPath, callback). The callback receives the updated dependency module.

hot.accept(deps, cb) accepting multiple dependencies

A module can accept updates from multiple direct dependencies by calling import.meta.hot.accept with an array of dependency paths and a callback. The callback receives an array where only updated modules are non-null. If an update is not successful (e.g. syntax error), the array is empty.

HMR module replacement limitations

Vite's HMR does not swap the originally imported module. If an HMR boundary module re-exports imports from a dependency, it is responsible for updating those re-exports, which must use 'let'. Importers up the chain from the boundary module will not be notified of the change. This simplified implementation is sufficient for most dev use cases.

hot.dispose(cb) for cleanup on update

A self-accepting module or a module that expects to be accepted by others can register a callback using hot.dispose to clean up persistent side effects created by its updated copy. The callback receives a data object.

hot.prune(cb) for cleanup when module removed

Register a callback with hot.prune that will be called when the module is no longer imported on the page. Compared to hot.dispose, this is used when source code cleans up side effects by itself on updates and only needs cleanup when removed from the page. Vite uses this for .css imports.

hot.data object persistence across HMR

Vite creates one import.meta.hot.data object for each module path that persists across successive instances of the same module during HMR. Mutations made during module execution or through the data argument passed to hot.dispose are visible to the next instance. When a module is pruned, hot.dispose and hot.prune callbacks receive the current data object, which Vite clears afterward. If the module is imported again later, it receives a new empty data object. Only mutation of the data object is supported; re-assignment of data itself is not supported.

hot.decline() is a backward compatibility noop

hot.decline() is currently a no-op and is there for backward compatibility. To indicate that a module is not hot-updatable, use hot.invalidate() instead.

hot.invalidate() to propagate updates to importers

A self-accepting module can call import.meta.hot.invalidate(message) during runtime if it cannot handle a HMR update, causing the HMR server to invalidate the importers of the caller as if the caller wasn't self-accepting. This logs a message in both browser console and terminal. You can pass an optional message to provide context. You should always call hot.accept first, even if you plan to call invalidate afterward, so the HMR client listens for future changes. It is recommended to call invalidate within the accept callback.

HMR events dispatched by Vite

Vite automatically dispatches the following HMR events: 'vite:beforeUpdate' (update about to be applied), 'vite:afterUpdate' (update has been applied), 'vite:beforeFullReload' (full reload about to occur), 'vite:beforePrune' (modules about to be pruned), 'vite:invalidate' (module invalidated), 'vite:error' (error occurred), 'vite:ws:disconnect' (WebSocket connection lost), 'vite:ws:connect' (WebSocket connection established or re-established).

hot.on(event, cb) to listen to HMR events

Listen to HMR events using hot.on(event, callback) where event is a CustomEventName. The callback is typed to receive InferCustomEventPayload for that event type.

hot.off(event, cb) to remove event listeners

Remove a callback from HMR event listeners using hot.off(event, callback).

hot.send(event, data) to send custom events

Send custom events back to Vite's dev server using hot.send(event, data) where event is a CustomEventName and data is optional. If called before WebSocket connection is established, the data will be buffered and sent once connected.

Example: hot.accept with callback for self-accepting

export const count = 1 if (import.meta.hot) { import.meta.hot.accept((newModule) => { if (newModule) { // newModule is undefined when SyntaxError happened console.log('updated: count is now ', newModule.count) } }) }

Example: hot.accept for single dependency

import { foo } from './foo.js' foo() if (import.meta.hot) { import.meta.hot.accept('./foo.js', (newFoo) => { // the callback receives the updated './foo.js' module newFoo?.foo() }) }

Example: hot.accept for multiple dependencies

import.meta.hot.accept( ['./foo.js', './bar.js'], ([newFooModule, newBarModule]) => { // The callback receives an array where only the updated module is // non null. If the update was not successful (syntax error for ex.), // the array is empty }, )

Example: hot.dispose for cleanup

function setupSideEffect() {} setupSideEffect() if (import.meta.hot) { import.meta.hot.dispose((data) => { // cleanup side effect }) }

Example: hot.prune for cleanup on removal

function setupOrReuseSideEffect() {} setupOrReuseSideEffect() if (import.meta.hot) { import.meta.hot.prune((data) => { // cleanup side effect }) }

Example: hot.data object mutation

// ok import.meta.hot.data.someValue = 'hello' // not supported import.meta.hot.data = { someValue: 'hello' }

Example: hot.invalidate called in accept callback

import.meta.hot.accept((module) => { // You may use the new module instance to decide whether to invalidate. if (cannotHandleUpdate(module)) { import.meta.hot.invalidate() } })

import.meta.hot.accept no longer accepts URL

In Vite 8, passing a URL to import.meta.hot.accept is no longer supported. Pass an id instead.

HMR API for frameworks

Vite provides an HMR API over native ESM. Frameworks with HMR capabilities can leverage the API to provide instant, precise updates without reloading the page or blowing away application state. Vite provides first-party HMR integrations for Vue Single File Components and React Fast Refresh.

How Vite's HMR works with native ESM

When you edit a file in Vite, Hot Module Replacement (HMR) over native ESM updates just that module in the browser without requiring a full page reload or waiting for a rebuild.

File case mismatch prevents HMR detection

If a file is imported with different casing than its actual filename (e.g., importing './Foo.js' when the file is 'foo.js'), Vite will detect the file change but HMR will not work. The import path must match the actual filename case exactly.

WSL2 file watching limitations

When running Vite with WSL2, Vite cannot watch file changes in some conditions. See the server.watch option documentation for more details.

Circular dependency causes full reload instead of HMR

If HMR is handled by Vite or a plugin but the module is within a circular dependency, a full reload will happen to recover the execution order. To solve this, try breaking the loop. Running vite --debug hmr will log the circular dependency path if a file change triggered it.

Give your agent this brain