diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 6d758e04cd807c..a4c1b9fd38a033 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -311,6 +311,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { } // fix#9534, prevent the importerModuleNode being stopped from propagating updates importerModule.isSelfAccepting = false + moduleGraph._hasResolveFailedErrorModules.add(importerModule) return this.error( `Failed to resolve import "${url}" from "${normalizePath( path.relative(process.cwd(), importerFile), diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index c5f5f8254941f5..3d5aedb2378836 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -163,6 +163,11 @@ export async function handleHMRUpdate( } const mods = new Set(moduleGraph.getModulesByFile(file)) + if (type === 'create') { + for (const mod of moduleGraph._hasResolveFailedErrorModules) { + mods.add(mod) + } + } if (type === 'create' || type === 'delete') { for (const mod of getAffectedGlobModules(file, server)) { mods.add(mod) diff --git a/packages/vite/src/node/server/moduleGraph.ts b/packages/vite/src/node/server/moduleGraph.ts index 02a30fb7af315a..c0554dd156481a 100644 --- a/packages/vite/src/node/server/moduleGraph.ts +++ b/packages/vite/src/node/server/moduleGraph.ts @@ -108,6 +108,9 @@ export class ModuleGraph { Promise | ModuleNode >() + /** @internal */ + _hasResolveFailedErrorModules = new Set() + constructor( private resolveId: ( url: string, @@ -229,6 +232,8 @@ export class ModuleGraph { ) } }) + + this._hasResolveFailedErrorModules.delete(mod) } invalidateAll(): void { diff --git a/playground/hmr/__tests__/hmr.spec.ts b/playground/hmr/__tests__/hmr.spec.ts index 5f82716df9203d..138cff2cb56b40 100644 --- a/playground/hmr/__tests__/hmr.spec.ts +++ b/playground/hmr/__tests__/hmr.spec.ts @@ -962,4 +962,23 @@ if (!isBuild) { editFile('css-deps/dep.js', (code) => code.replace(`red`, `green`)) await untilUpdated(() => getColor('.css-deps'), 'green') }) + + test('hmr should happen after missing file is created', async () => { + const file = 'missing-file/a.js' + const code = 'console.log("a.js")' + + await untilBrowserLogAfter( + () => + page.goto(viteTestUrl + '/missing-file/index.html', { + waitUntil: 'load', + }), + /connected/, // wait for HMR connection + ) + + await untilBrowserLogAfter(async () => { + const loadPromise = page.waitForEvent('load') + addFile(file, code) + await loadPromise + }, [/connected/, 'a.js']) + }) } diff --git a/playground/hmr/missing-file/index.html b/playground/hmr/missing-file/index.html new file mode 100644 index 00000000000000..cfbd07a1e44286 --- /dev/null +++ b/playground/hmr/missing-file/index.html @@ -0,0 +1,2 @@ +
Page
+ diff --git a/playground/hmr/missing-file/main.js b/playground/hmr/missing-file/main.js new file mode 100644 index 00000000000000..999801e4dd1061 --- /dev/null +++ b/playground/hmr/missing-file/main.js @@ -0,0 +1 @@ +import './a.js'