Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hmr): invalidate message #10946

Merged
merged 2 commits into from Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.log(
`[vite] invalidate ${ownerPath}${message ? `: ${message}` : ''}`
)
ArnaudBarre marked this conversation as resolved.
Show resolved Hide resolved
},

// 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