diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index 42badca778b453..e72b1ebdf05338 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -144,13 +144,21 @@ export function updateModules( let needFullReload = false for (const mod of modules) { - moduleGraph.invalidateModule(mod, invalidatedModules, timestamp, true) + const boundaries: { boundary: ModuleNode; acceptedVia: ModuleNode }[] = [] + const hasDeadEnd = propagateUpdate(mod, traversedModules, boundaries) + + moduleGraph.invalidateModule( + mod, + invalidatedModules, + timestamp, + true, + boundaries.map((b) => b.boundary), + ) + if (needFullReload) { continue } - const boundaries: { boundary: ModuleNode; acceptedVia: ModuleNode }[] = [] - const hasDeadEnd = propagateUpdate(mod, traversedModules, boundaries) if (hasDeadEnd) { needFullReload = true continue diff --git a/packages/vite/src/node/server/moduleGraph.ts b/packages/vite/src/node/server/moduleGraph.ts index 804e38bc2c3cc7..9bc413ad4af8af 100644 --- a/packages/vite/src/node/server/moduleGraph.ts +++ b/packages/vite/src/node/server/moduleGraph.ts @@ -121,6 +121,7 @@ export class ModuleGraph { seen: Set = new Set(), timestamp: number = Date.now(), isHmr: boolean = false, + hmrBoundaries: ModuleNode[] = [], ): void { if (seen.has(mod)) { return @@ -139,6 +140,11 @@ export class ModuleGraph { mod.ssrTransformResult = null mod.ssrModule = null mod.ssrError = null + + // Fix #3033 + if (hmrBoundaries.includes(mod)) { + return + } mod.importers.forEach((importer) => { if (!importer.acceptedHmrDeps.has(mod)) { this.invalidateModule(importer, seen, timestamp, isHmr) diff --git a/playground/hmr/__tests__/hmr.spec.ts b/playground/hmr/__tests__/hmr.spec.ts index a7fbbad80ae90e..a80f711dbdcd2a 100644 --- a/playground/hmr/__tests__/hmr.spec.ts +++ b/playground/hmr/__tests__/hmr.spec.ts @@ -786,4 +786,14 @@ if (import.meta.hot) { ) await untilUpdated(() => el.textContent(), '2') }) + + test('issue-3033', async () => { + await page.goto(viteTestUrl + '/issue-3033/index.html') + const el = await page.$('.issue-3033') + expect(await el.textContent()).toBe('c') + editFile('issue-3033/c.js', (code) => + code.replace(`export const c = 'c'`, `export const c = 'cc'`), + ) + await untilUpdated(() => el.textContent(), 'cc') + }) } diff --git a/playground/hmr/issue-3033/a.js b/playground/hmr/issue-3033/a.js new file mode 100644 index 00000000000000..a559b739d9f253 --- /dev/null +++ b/playground/hmr/issue-3033/a.js @@ -0,0 +1,5 @@ +import { b } from './b' + +export const a = { + b, +} diff --git a/playground/hmr/issue-3033/b.js b/playground/hmr/issue-3033/b.js new file mode 100644 index 00000000000000..4f5a135418728c --- /dev/null +++ b/playground/hmr/issue-3033/b.js @@ -0,0 +1,7 @@ +import { c } from './c' + +const b = { + c, +} + +export { b } diff --git a/playground/hmr/issue-3033/c.js b/playground/hmr/issue-3033/c.js new file mode 100644 index 00000000000000..18ebe62aa9f0a7 --- /dev/null +++ b/playground/hmr/issue-3033/c.js @@ -0,0 +1,12 @@ +import './b' + +export const c = 'c' + +function render(content) { + document.querySelector('.issue-3033').textContent = content +} +render(c) + +import.meta.hot?.accept((nextExports) => { + render(nextExports.c) +}) diff --git a/playground/hmr/issue-3033/index.html b/playground/hmr/issue-3033/index.html new file mode 100644 index 00000000000000..aa40111b26c214 --- /dev/null +++ b/playground/hmr/issue-3033/index.html @@ -0,0 +1,2 @@ + +
diff --git a/playground/hmr/issue-3033/index.js b/playground/hmr/issue-3033/index.js new file mode 100644 index 00000000000000..604c0a3518932b --- /dev/null +++ b/playground/hmr/issue-3033/index.js @@ -0,0 +1,3 @@ +import { a } from './a' + +console.log(a)