Skip to content

Commit 2b61cc3

Browse files
authored
fix(hmr): trigger page reload when calling invalidate on root module (#16636)
1 parent 65eb48f commit 2b61cc3

File tree

6 files changed

+33
-6
lines changed

6 files changed

+33
-6
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ export function updateModules(
228228
const updates: Update[] = []
229229
const invalidatedModules = new Set<ModuleNode>()
230230
const traversedModules = new Set<ModuleNode>()
231-
let needFullReload: HasDeadEnd = false
231+
// Modules could be empty if a root module is invalidated via import.meta.hot.invalidate()
232+
let needFullReload: HasDeadEnd = modules.length === 0
232233

233234
for (const mod of modules) {
234235
const boundaries: PropagationBoundary[] = []

playground/hmr/__tests__/hmr.spec.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ if (!isBuild) {
154154
})
155155

156156
test('invalidate', async () => {
157-
const el = await page.$('.invalidation')
157+
const el = await page.$('.invalidation-parent')
158158
await untilBrowserLogAfter(
159159
() =>
160160
editFile('invalidation/child.js', (code) =>
@@ -182,7 +182,7 @@ if (!isBuild) {
182182
page2 = await browser.newPage()
183183
await page2.goto(viteTestUrl)
184184

185-
const el = await page.$('.invalidation')
185+
const el = await page.$('.invalidation-parent')
186186
await untilBrowserLogAfter(
187187
() =>
188188
editFile('invalidation/child.js', (code) =>
@@ -208,6 +208,15 @@ if (!isBuild) {
208208
}
209209
})
210210

211+
test('invalidate on root triggers page reload', async () => {
212+
editFile('invalidation/root.js', (code) => code.replace('Init', 'Updated'))
213+
await page.waitForEvent('load')
214+
await untilUpdated(
215+
async () => (await page.$('.invalidation-root')).textContent(),
216+
'Updated',
217+
)
218+
})
219+
211220
test('soft invalidate', async () => {
212221
const el = await page.$('.soft-invalidation')
213222
expect(await el.textContent()).toBe(

playground/hmr/hmr.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { virtual } from 'virtual:file'
22
import { foo as depFoo, nestedFoo } from './hmrDep'
33
import './importing-updated'
4-
import './invalidation/parent'
54
import './file-delete-restore'
65
import './optional-chaining/parent'
76
import './intermediate-file-delete'

playground/hmr/index.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
</div>
88
<button class="virtual-update">update virtual module</button>
99

10+
<script type="module" src="./invalidation/root.js"></script>
1011
<script type="module" src="./hmr.ts"></script>
1112
<style>
1213
.import-image {
@@ -24,7 +25,8 @@
2425
<div class="toRemove"></div>
2526
<div class="virtual"></div>
2627
<div class="soft-invalidation"></div>
27-
<div class="invalidation"></div>
28+
<div class="invalidation-parent"></div>
29+
<div class="invalidation-root"></div>
2830
<div class="custom-communication"></div>
2931
<div class="css-prev"></div>
3032
<div class="css-post"></div>

playground/hmr/invalidation/parent.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ if (import.meta.hot) {
66

77
console.log('(invalidation) parent is executing')
88

9-
document.querySelector('.invalidation').innerHTML = value
9+
document.querySelector('.invalidation-parent').innerHTML = value

playground/hmr/invalidation/root.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import './parent.js'
2+
3+
if (import.meta.hot) {
4+
// Need to accept, to register a callback for HMR
5+
import.meta.hot.accept(() => {
6+
// Triggers full page reload because no importers
7+
import.meta.hot.invalidate()
8+
})
9+
}
10+
11+
const root = document.querySelector('.invalidation-root')
12+
13+
// Non HMR-able behaviour
14+
if (!root.innerHTML) {
15+
root.innerHTML = 'Init'
16+
}

0 commit comments

Comments
 (0)