Skip to content

Commit b314771

Browse files
committedJan 2, 2021
refactor(hmr): pass context object to handleHotUpdate plugin hook
instead of multiple args BREAKING CHANGE: `handleHotUpdate` plugin hook now receives a single `HmrContext` argument instead of multiple args.
1 parent b039fa5 commit b314771

File tree

8 files changed

+57
-36
lines changed

8 files changed

+57
-36
lines changed
 

‎docs/guide/api-hmr.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# HMR API
22

33
:::tip Note
4+
This is the client HMR API. For handling HMR update in plugins, see [handleHotUpdate](./api-plugin#handlehotupdate).
5+
46
The manual HMR API is primarly intended for framework and tooling authors. As an end user, HMR is likely already handled for you in the framework specific starter templates.
57
:::
68

‎docs/guide/api-plugin.md

+15-9
Original file line numberDiff line numberDiff line change
@@ -271,17 +271,23 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo
271271

272272
### `handleHotUpdate`
273273

274-
- **Type:** `(file: string, mods: Array<ModuleNode>, read: () => string | Promise<string>, server: ViteDevServer) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>`
274+
- **Type:** `(ctx: HmrContext) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>`
275275

276-
Perform custom HMR update handling. The hook receives the following arguments:
277-
278-
1. The changed file path
276+
Perform custom HMR update handling. The hook receives a context object with the following signature:
279277

280-
2. An array of modules that are affected by the changed file. It's an array because a single file may map to multiple served modules (e.g. Vue SFCs).
278+
```ts
279+
interface HmrContext {
280+
file: string
281+
timestamp: number
282+
modules: Array<ModuleNode>
283+
read: () => string | Promise<string>
284+
server: ViteDevServer
285+
}
286+
```
281287

282-
3. An async read function that returns the content of the file. This 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` will return empty content. The read function passed in normalizes this behavior.
288+
- `modules` is an array of modules that are affected by the changed file. It's an array because a single file may map to multiple served modules (e.g. Vue SFCs).
283289

284-
4. The [`ViteDevServer`](./api-javascript#vitedevserver) instance.
290+
- `read` is an async read function that returns the content of the file. This 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` will return empty content. The read function passed in normalizes this behavior.
285291

286292
The hook can choose to:
287293

@@ -290,7 +296,7 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo
290296
- Return an empty array and perform complete custom HMR handling by sending custom events to the client:
291297

292298
```js
293-
handleHotUpdate(file, mods, read, server) {
299+
handleHotUpdate({ server }) {
294300
server.ws.send({
295301
type: 'custom',
296302
event: 'special-update',
@@ -304,7 +310,7 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo
304310
305311
```js
306312
if (import.meta.hot) {
307-
import.meta.hot.on('special-update', data => {
313+
import.meta.hot.on('special-update', (data) => {
308314
// perform custom update
309315
})
310316
}

‎packages/playground/hmr/vite.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = {
55
plugins: [
66
{
77
name: 'mock-custom',
8-
async handleHotUpdate(file, mods, read, server) {
8+
async handleHotUpdate({ file, read, server }) {
99
if (file.endsWith('customFile.js')) {
1010
const content = await read()
1111
const msg = content.match(/export const msg = '(\w+)'/)[1]

‎packages/plugin-vue/src/handleHotUpdate.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ import {
66
setPrevDescriptor
77
} from './utils/descriptorCache'
88
import { getResolvedScript, setResolvedScript } from './script'
9-
import { ModuleNode, ViteDevServer } from 'vite'
9+
import { ModuleNode, HmrContext } from 'vite'
1010

1111
const debug = _debug('vite:hmr')
1212

1313
/**
1414
* Vite-specific HMR handling
1515
*/
16-
export async function handleHotUpdate(
17-
file: string,
18-
modules: ModuleNode[],
19-
read: () => string | Promise<string>,
20-
server: ViteDevServer
21-
): Promise<ModuleNode[] | void> {
16+
export async function handleHotUpdate({
17+
file,
18+
modules,
19+
read,
20+
server
21+
}: HmrContext): Promise<ModuleNode[] | void> {
2222
const prevDescriptor = getDescriptor(file, false)
2323
if (!prevDescriptor) {
2424
// file hasn't been requested yet (e.g. async component)

‎packages/plugin-vue/src/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin {
6565
return {
6666
name: 'vite:vue',
6767

68-
handleHotUpdate(file, mods, read, server) {
69-
if (!filter(file)) {
68+
handleHotUpdate(ctx) {
69+
if (!filter(ctx.file)) {
7070
return
7171
}
72-
return handleHotUpdate(file, mods, read, server)
72+
return handleHotUpdate(ctx)
7373
},
7474

7575
config(config) {

‎packages/vite/src/node/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export type { PluginContainer } from './server/pluginContainer'
2828
export type { ModuleGraph, ModuleNode } from './server/moduleGraph'
2929
export type { ProxyOptions } from './server/middlewares/proxy'
3030
export type { TransformResult } from './server/transformRequest'
31-
export type { HmrOptions } from './server/hmr'
31+
export type { HmrOptions, HmrContext } from './server/hmr'
3232
export type {
3333
HMRPayload,
3434
ConnectedPayload,

‎packages/vite/src/node/plugin.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { UserConfig } from './config'
22
import { Plugin as RollupPlugin } from 'rollup'
3-
import { ServerHook, ViteDevServer } from './server'
3+
import { ServerHook } from './server'
44
import { IndexHtmlTransform } from './plugins/html'
55
import { ModuleNode } from './server/moduleGraph'
66
import { ResolvedConfig } from './'
7+
import { HmrContext } from './server/hmr'
78

89
/**
910
* Vite plugins extends the Rollup plugin interface with a few extra
@@ -79,8 +80,8 @@ export interface Plugin extends RollupPlugin {
7980
transformIndexHtml?: IndexHtmlTransform
8081
/**
8182
* Perform custom handling of HMR updates.
82-
* The handler receives the changed filename, a list of modules affected by
83-
* the file change, and the dev server instance.
83+
* The handler receives a context containing changed filename, timestamp, a
84+
* list of modules affected by the file change, and the dev server instance.
8485
*
8586
* - The hook can return a filtered list of modules to narrow down the update.
8687
* e.g. for a Vue SFC, we can narrow down the part to update by comparing
@@ -93,9 +94,6 @@ export interface Plugin extends RollupPlugin {
9394
* normal.
9495
*/
9596
handleHotUpdate?: (
96-
file: string,
97-
mods: Array<ModuleNode>,
98-
read: () => string | Promise<string>,
99-
server: ViteDevServer
97+
ctx: HmrContext
10098
) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>
10199
}

‎packages/vite/src/node/server/hmr.ts

+23-8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ export interface HmrOptions {
2222
overlay?: boolean
2323
}
2424

25+
export interface HmrContext {
26+
file: string
27+
timestamp: number
28+
modules: Array<ModuleNode>
29+
read: () => string | Promise<string>
30+
server: ViteDevServer
31+
}
32+
2533
export async function handleHMRUpdate(
2634
file: string,
2735
server: ViteDevServer
@@ -59,17 +67,25 @@ export async function handleHMRUpdate(
5967
const mods = moduleGraph.getModulesByFile(file)
6068

6169
// check if any plugin wants to perform custom HMR handling
62-
let filteredMods = mods ? [...mods] : []
63-
const read = () => readModifiedFile(file)
70+
const timestamp = Date.now()
71+
const hmrContext: HmrContext = {
72+
file,
73+
timestamp,
74+
modules: mods ? [...mods] : [],
75+
read: () => readModifiedFile(file),
76+
server
77+
}
78+
6479
for (const plugin of config.plugins) {
6580
if (plugin.handleHotUpdate) {
66-
filteredMods =
67-
(await plugin.handleHotUpdate(file, filteredMods, read, server)) ||
68-
filteredMods
81+
const filteredModules = await plugin.handleHotUpdate(hmrContext)
82+
if (filteredModules) {
83+
hmrContext.modules = filteredModules
84+
}
6985
}
7086
}
7187

72-
if (!filteredMods.length) {
88+
if (!hmrContext.modules.length) {
7389
// html file cannot be hot updated
7490
if (file.endsWith('.html')) {
7591
config.logger.info(chalk.green(`page reload `) + chalk.dim(shortFile), {
@@ -87,10 +103,9 @@ export async function handleHMRUpdate(
87103
return
88104
}
89105

90-
const timestamp = Date.now()
91106
const updates: Update[] = []
92107

93-
for (const mod of filteredMods) {
108+
for (const mod of hmrContext.modules) {
94109
const boundaries = new Set<{
95110
boundary: ModuleNode
96111
acceptedVia: ModuleNode

0 commit comments

Comments
 (0)
Please sign in to comment.