Skip to content

Commit

Permalink
feat(hmr): invalidate message (#10946)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudBarre committed Nov 17, 2022
1 parent d9779c7 commit 0d73473
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 12 deletions.
6 changes: 3 additions & 3 deletions docs/guide/api-hmr.md
Expand Up @@ -30,7 +30,7 @@ interface ViteHotContext {

dispose(cb: (data: any) => void): void
decline(): void
invalidate(): void
invalidate(message?: string): void

// `InferCustomEventPayload` provides types for built-in Vite events
on<T extends string>(
Expand Down Expand Up @@ -123,9 +123,9 @@ The `import.meta.hot.data` object is persisted across different instances of the
Calling `import.meta.hot.decline()` indicates this module is not hot-updatable, and the browser should perform a full reload if this module is encountered while propagating HMR updates.
## `hot.invalidate()`
## `hot.invalidate(message?: string)`
A self-accepting module may realize during runtime that it can't handle a HMR update, and so the update needs to be forcefully propagated to importers. By calling `import.meta.hot.invalidate()`, the HMR server will invalidate the importers of the caller, as if the caller wasn't self-accepting.
A self-accepting module may realize during runtime that it can't handle a HMR update, and so the update needs to be forcefully propagated to importers. By calling `import.meta.hot.invalidate()`, the HMR server will invalidate the importers of the caller, as if the caller wasn't self-accepting. This will log a message both in the browser console and in the terminal. You can pass a message to give some context on why the invalidation happened.
Note that you should always call `import.meta.hot.accept` even if you plan to call `invalidate` immediately afterwards, or else the HMR client won't listen for future changes to the self-accepting module. To communicate your intent clearly, we recommend calling `invalidate` within the `accept` callback like so:
Expand Down
9 changes: 6 additions & 3 deletions packages/vite/src/client/client.ts
Expand Up @@ -558,9 +558,12 @@ export function createHotContext(ownerPath: string): ViteHotContext {
decline() {},

// tell the server to re-perform hmr propagation from this module as root
invalidate() {
notifyListeners('vite:invalidate', { path: ownerPath })
this.send('vite:invalidate', { path: ownerPath })
invalidate(message) {
notifyListeners('vite:invalidate', { path: ownerPath, message })
this.send('vite:invalidate', { path: ownerPath, message })
console.debug(
`[vite] invalidate ${ownerPath}${message ? `: ${message}` : ''}`
)
},

// custom events
Expand Down
7 changes: 4 additions & 3 deletions packages/vite/src/node/server/hmr.ts
Expand Up @@ -128,7 +128,8 @@ export function updateModules(
file: string,
modules: ModuleNode[],
timestamp: number,
{ config, ws }: ViteDevServer
{ config, ws }: ViteDevServer,
afterInvalidation?: boolean
): void {
const updates: Update[] = []
const invalidatedModules = new Set<ModuleNode>()
Expand Down Expand Up @@ -166,7 +167,7 @@ export function updateModules(

if (needFullReload) {
config.logger.info(colors.green(`page reload `) + colors.dim(file), {
clear: true,
clear: !afterInvalidation,
timestamp: true
})
ws.send({
Expand All @@ -183,7 +184,7 @@ export function updateModules(
config.logger.info(
colors.green(`hmr update `) +
colors.dim([...new Set(updates.map((u) => u.path))].join(', ')),
{ clear: true, timestamp: true }
{ clear: !afterInvalidation, timestamp: true }
)
ws.send({
type: 'update',
Expand Down
16 changes: 14 additions & 2 deletions packages/vite/src/node/server/index.ts
Expand Up @@ -505,11 +505,23 @@ export async function createServer(
handleFileAddUnlink(normalizePath(file), server)
})

ws.on('vite:invalidate', async ({ path }: InvalidatePayload) => {
ws.on('vite:invalidate', async ({ path, message }: InvalidatePayload) => {
const mod = moduleGraph.urlToModuleMap.get(path)
if (mod && mod.isSelfAccepting && mod.lastHMRTimestamp > 0) {
config.logger.info(
colors.yellow(`hmr invalidate `) +
colors.dim(path) +
(message ? ` ${message}` : ''),
{ timestamp: true }
)
const file = getShortName(mod.file!, config.root)
updateModules(file, [...mod.importers], mod.lastHMRTimestamp, server)
updateModules(
file,
[...mod.importers],
mod.lastHMRTimestamp,
server,
true
)
}
})

Expand Down
1 change: 1 addition & 0 deletions packages/vite/types/customEvent.d.ts
Expand Up @@ -16,6 +16,7 @@ export interface CustomEventMap {

export interface InvalidatePayload {
path: string
message: string | undefined
}

export type InferCustomEventPayload<T extends string> =
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/types/hot.d.ts
Expand Up @@ -23,7 +23,7 @@ export interface ViteHotContext {

dispose(cb: (data: any) => void): void
decline(): void
invalidate(): void
invalidate(message?: string): void

on<T extends string>(
event: T,
Expand Down
1 change: 1 addition & 0 deletions playground/hmr/__tests__/hmr.spec.ts
Expand Up @@ -145,6 +145,7 @@ if (!isBuild) {
expect(browserLogs).toMatchObject([
'>>> vite:beforeUpdate -- update',
'>>> vite:invalidate -- /invalidation/child.js',
'[vite] invalidate /invalidation/child.js',
'[vite] hot updated: /invalidation/child.js',
'>>> vite:afterUpdate -- update',
'>>> vite:beforeUpdate -- update',
Expand Down
2 changes: 2 additions & 0 deletions playground/react/__tests__/react.spec.ts
Expand Up @@ -54,6 +54,7 @@ if (!isBuild) {
)
await untilUpdated(() => page.textContent('#parent'), 'Updated')
expect(browserLogs).toMatchObject([
'[vite] invalidate /hmr/no-exported-comp.jsx',
'[vite] hot updated: /hmr/no-exported-comp.jsx',
'[vite] hot updated: /hmr/parent.jsx',
'Parent rendered'
Expand All @@ -79,6 +80,7 @@ if (!isBuild) {
'context provider updated'
)
expect(browserLogs).toMatchObject([
'[vite] invalidate /context/CountProvider.jsx',
'[vite] hot updated: /context/CountProvider.jsx',
'[vite] hot updated: /App.jsx',
'[vite] hot updated: /context/ContextButton.jsx',
Expand Down

0 comments on commit 0d73473

Please sign in to comment.